mirror of https://github.com/dynup/kpatch
create-diff-object: refactor dynrela conversion
The dynrela (aka .klp.rela) conversion logic is notoriously complex and fragile. Simplify it and improve the comments. This is mainly a cosmetic change. In theory it shouldn't change functionality or break anything. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
This commit is contained in:
parent
d2089a4d72
commit
0a3e6c5f42
|
@ -171,6 +171,32 @@ static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On ppc64le, when a function references data, it does so indirectly, via the
|
||||
* .toc section. So there are *two* levels of relas:
|
||||
*
|
||||
* 1) the original function rela, referring to the .toc section; and
|
||||
*
|
||||
* 2) the .toc section rela, referring to the data needed by the function.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries:
|
||||
* ...
|
||||
* 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138
|
||||
* 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138
|
||||
*
|
||||
* Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries:
|
||||
* ...
|
||||
* 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8
|
||||
*
|
||||
* The below function takes the "first level" rela as input, and, if it refers
|
||||
* to .toc, returns the "second level" rela, which is the one that refers to
|
||||
* the actual data symbol.
|
||||
*
|
||||
* In some rare cases, a .toc entry has constant data, and thus has no
|
||||
* corresponding rela. In that case, NULL is returned.
|
||||
*/
|
||||
static struct rela *toc_rela(const struct rela *rela)
|
||||
{
|
||||
if (rela->type != R_PPC64_TOC16_HA &&
|
||||
|
@ -2746,16 +2772,15 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|||
sym->parent)
|
||||
continue;
|
||||
|
||||
if (sym->bind == STB_LOCAL) {
|
||||
if (!lookup_local_symbol(table, sym->name, &symbol))
|
||||
ERROR("lookup_local_symbol %s", sym->name);
|
||||
} else {
|
||||
if (!lookup_global_symbol(table, sym->name, &symbol))
|
||||
ERROR("lookup_global_symbol %s", sym->name);
|
||||
}
|
||||
if (!lookup_symbol(table, sym->name, &symbol))
|
||||
ERROR("can't find symbol '%s' in symbol table", sym->name);
|
||||
|
||||
log_debug("lookup for %s @ 0x%016lx len %lu\n",
|
||||
sym->name, symbol.addr, symbol.size);
|
||||
if (sym->bind == STB_LOCAL && symbol.global)
|
||||
ERROR("can't find local symbol '%s' in symbol table", sym->name);
|
||||
|
||||
log_debug("lookup for %s: obj=%s sympos=%lu size=%lu",
|
||||
sym->name, symbol.objname, symbol.sympos,
|
||||
symbol.size);
|
||||
|
||||
/*
|
||||
* Convert global symbols to local so other objects in the
|
||||
|
@ -2821,76 +2846,178 @@ static int kpatch_is_core_module_symbol(char *name)
|
|||
!strcmp(name, "kpatch_shadow_get"));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the patched code refers to a symbol, for example, calls a function
|
||||
* or stores a pointer to a function somewhere, the address of that symbol
|
||||
* must be resolved somehow before the patch is applied. The symbol may be
|
||||
* present in the original code too, so the patch may refer either to that
|
||||
* version of the symbol (dynrela is used for that) or to its patched
|
||||
* version directly (with a normal relocation).
|
||||
*
|
||||
* Dynrelas may be needed for the symbols not present in this object file
|
||||
* (rela->sym->sec is NULL), because it is unknown if the patched versions
|
||||
* of these symbols exist and where they are.
|
||||
*
|
||||
* The patched code can usually refer to a symbol from this object file
|
||||
* directly. If it is a function, this may also improve performance because
|
||||
* it will not be needed to call the original function first, find the
|
||||
* patched one and then use Ftrace to pass control to it.
|
||||
*
|
||||
* There is an exception though, at least on x86. It is safer to use
|
||||
* a dynrela if the patched code stores a pointer to a function somewhere
|
||||
* (relocation of type R_X86_64_32S). The function could be used as
|
||||
* a callback and some kinds of callbacks are called asynchronously. If
|
||||
* the patch module sets such callback and is unloaded shortly after,
|
||||
* the kernel could try to call the function via an invalid pointer and
|
||||
* would crash. With dynrela, the kernel would call the original function
|
||||
* in that case.
|
||||
*/
|
||||
static int function_ptr_rela(const struct rela *rela)
|
||||
{
|
||||
const struct rela *rela_toc = toc_rela(rela);
|
||||
|
||||
return (rela_toc && rela_toc->sym->type == STT_FUNC &&
|
||||
!rela_toc->sym->parent &&
|
||||
/* skip switch table on PowerPC */
|
||||
rela_toc->addend == (int)rela_toc->sym->sym.st_value &&
|
||||
(rela->type == R_X86_64_32S ||
|
||||
rela->type == R_PPC64_TOC16_HA ||
|
||||
rela->type == R_PPC64_TOC16_LO_DS));
|
||||
}
|
||||
|
||||
static int may_need_dynrela(const struct rela *rela)
|
||||
static bool need_dynrela(struct lookup_table *table, const struct rela *rela)
|
||||
{
|
||||
struct lookup_result symbol;
|
||||
|
||||
/*
|
||||
* References to .TOC. are treated specially by the module loader and
|
||||
* These references are treated specially by the module loader and
|
||||
* should never be converted to dynrelas.
|
||||
*/
|
||||
if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO ||
|
||||
rela->type == R_PPC64_REL64)
|
||||
return 0;
|
||||
|
||||
if (!rela->sym->sec)
|
||||
return 1;
|
||||
rela->type == R_PPC64_REL64 || rela->type == R_PPC64_ENTRY)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Nested functions used as callbacks are a special case.
|
||||
* They are not supposed to be visible outside of the
|
||||
* function that defines them. Their names may differ in
|
||||
* the original and the patched kernels which makes it
|
||||
* difficult to use dynrelas. Fortunately, nested functions
|
||||
* are rare and are unlikely to be used as asynchronous
|
||||
* callbacks, so the patched code can refer to them directly.
|
||||
* It seems, one can only distinguish such functions by their
|
||||
* names containing a dot. Other kinds of functions with
|
||||
* such names (e.g. optimized copies of functions) are
|
||||
* unlikely to be used as callbacks.
|
||||
* On powerpc, the function prologue generated by GCC 6 has the
|
||||
* sequence:
|
||||
*
|
||||
* .globl my_func
|
||||
* .type my_func, @function
|
||||
* .quad .TOC.-my_func
|
||||
* my_func:
|
||||
* .reloc ., R_PPC64_ENTRY ; optional
|
||||
* ld r2,-8(r12)
|
||||
* add r2,r2,r12
|
||||
* .localentry my_func, .-my_func
|
||||
*
|
||||
* The R_PPC64_ENTRY is optional and its symbol might have an empty
|
||||
* name. Leave it as a normal rela.
|
||||
*/
|
||||
return (function_ptr_rela(rela) &&
|
||||
toc_rela(rela)->sym->status != NEW &&
|
||||
!strchr(toc_rela(rela)->sym->name, '.'));
|
||||
if (rela->type == R_PPC64_ENTRY)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Allow references to core module symbols to remain as normal
|
||||
* relas. They should be exported.
|
||||
*/
|
||||
if (kpatch_is_core_module_symbol(rela->sym->name))
|
||||
return false;
|
||||
|
||||
if (rela->sym->sec) {
|
||||
/*
|
||||
* Internal symbols usually don't need dynrelas, because they
|
||||
* live in the patch module and can be relocated normally.
|
||||
*
|
||||
* There's one exception: function pointers.
|
||||
*
|
||||
* If the rela references a function pointer, we convert it to
|
||||
* a dynrela, so that the function pointer will refer to the
|
||||
* original function rather than the patched function. This
|
||||
* can prevent crashes in cases where the function pointer is
|
||||
* called asynchronously after the patch module has been
|
||||
* unloaded.
|
||||
*/
|
||||
if (!function_ptr_rela(rela))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Function pointers which refer to _nested_ functions are a
|
||||
* special case. They are not supposed to be visible outside
|
||||
* of the function that defines them. Their names may differ
|
||||
* in the original and the patched kernels which makes it
|
||||
* difficult to use dynrelas. Fortunately, nested functions
|
||||
* are rare and are unlikely to be used as asynchronous
|
||||
* callbacks, so the patched code can refer to them directly.
|
||||
* It seems, one can only distinguish such functions by their
|
||||
* names containing a dot. Other kinds of functions with such
|
||||
* names (e.g. optimized copies of functions) are unlikely to
|
||||
* be used as callbacks.
|
||||
*
|
||||
* Function pointers to *new* functions don't have this issue,
|
||||
* just use a normal rela for them.
|
||||
*/
|
||||
return toc_rela(rela)->sym->status != NEW &&
|
||||
!strchr(toc_rela(rela)->sym->name, '.');
|
||||
}
|
||||
|
||||
if (!lookup_symbol(table, rela->sym->name, &symbol)) {
|
||||
/*
|
||||
* Assume the symbol lives in another .o in the patch module.
|
||||
* A normal rela should work.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rela->sym->bind == STB_LOCAL) {
|
||||
|
||||
if (symbol.global)
|
||||
ERROR("can't find local symbol '%s' in symbol table",
|
||||
rela->sym->name);
|
||||
|
||||
/*
|
||||
* The symbol is (formerly) local. Use a dynrela to access the
|
||||
* original version of the symbol in the patched object.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
if (symbol.exported) {
|
||||
|
||||
if (is_gcc6_localentry_bundled_sym(rela->sym)) {
|
||||
/*
|
||||
* On powerpc, the symbol is global and exported, but
|
||||
* it was also in the changed object file. In this
|
||||
* case the rela refers to the 'localentry' point, so a
|
||||
* normal rela wouldn't work. Force a dynrela so it
|
||||
* can be handled correctly by the livepatch relocation
|
||||
* code.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(symbol.objname, "vmlinux")) {
|
||||
/*
|
||||
* The symbol is exported by vmlinux. Use a normal
|
||||
* rela.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The symbol is exported by the to-be-patched module, or by
|
||||
* another module which the patched module depends on. Use a
|
||||
* dynrela because of late module loading: the patch module may
|
||||
* be loaded before the to-be-patched (or other) module.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
if (symbol.global) {
|
||||
/*
|
||||
* The symbol is global in the to-be-patched object, but not
|
||||
* exported. Use a dynrela to work around the fact that it's
|
||||
* an unexported sybmbol.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The symbol is global and not exported, but it's not in the parent
|
||||
* object. The only explanation is that it's defined in another object
|
||||
* in the patch module. A normal rela should resolve it.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* kpatch_create_intermediate_sections()
|
||||
*
|
||||
* The primary purpose of this function is to convert some relas (also known as
|
||||
* relocations) to dynrelas (also known as dynamic relocations or livepatch
|
||||
* relocations or klp relas).
|
||||
*
|
||||
* If the patched code refers to a symbol, for example, if it calls a function
|
||||
* or stores a pointer to a function somewhere or accesses some global data,
|
||||
* the address of that symbol must be resolved somehow before the patch is
|
||||
* applied.
|
||||
*
|
||||
* If the symbol lives outside the patch module, and if it's not exported by
|
||||
* vmlinux (e.g., with EXPORT_SYMBOL) then the rela needs to be converted to a
|
||||
* dynrela so the livepatch code can resolve it at runtime.
|
||||
*/
|
||||
static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
||||
struct lookup_table *table,
|
||||
char *objname,
|
||||
|
@ -2903,8 +3030,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
struct kpatch_symbol *ksyms;
|
||||
struct kpatch_relocation *krelas;
|
||||
struct lookup_result symbol;
|
||||
char *sym_objname;
|
||||
int vmlinux, external;
|
||||
bool vmlinux;
|
||||
|
||||
vmlinux = !strcmp(objname, "vmlinux");
|
||||
|
||||
|
@ -2916,25 +3042,22 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
if (!strcmp(sec->name, ".rela.kpatch.funcs"))
|
||||
continue;
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
nr++; /* upper bound on number of kpatch relas and symbols */
|
||||
|
||||
/* upper bound on number of kpatch relas and symbols */
|
||||
nr++;
|
||||
|
||||
/*
|
||||
* Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries:
|
||||
* ...
|
||||
* 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8
|
||||
* We set 'need_dynrela' here in the first pass because
|
||||
* the .toc section's 'need_dynrela' values are
|
||||
* dependent on all the other sections. Otherwise, if
|
||||
* we did this analysis in the second pass, we'd have
|
||||
* to convert .toc dynrelas at the very end.
|
||||
*
|
||||
* Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries:
|
||||
* ...
|
||||
* 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138
|
||||
* 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138
|
||||
*
|
||||
* On PowerPC, may_need_dynrela() should be using rela's reference in .rela.toc for
|
||||
* the rela like in the example, where the sym name is .toc + offset. In such case,
|
||||
* the checks are performed on both rela and its reference in .rela.toc. Where the
|
||||
* rela is checked for rela->type and its corresponding rela in .rela.toc for function
|
||||
* pointer/switch label. If rela->need_dynrela needs to be set, it's referenced rela
|
||||
* in (.rela.toc)->need_dynrela is set, as they represent the function sym.
|
||||
* Specifically, this is needed for the powerpc
|
||||
* internal symbol function pointer check which is done
|
||||
* via .toc indirection in need_dynrela().
|
||||
*/
|
||||
if (may_need_dynrela(rela))
|
||||
if (need_dynrela(table, rela))
|
||||
toc_rela(rela)->need_dynrela = 1;
|
||||
}
|
||||
}
|
||||
|
@ -2973,129 +3096,13 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
if (!rela->need_dynrela)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Allow references to core module symbols to remain as
|
||||
* normal relas, since the core module may not be
|
||||
* compiled into the kernel, and they should be
|
||||
* exported anyway.
|
||||
*/
|
||||
if (kpatch_is_core_module_symbol(rela->sym->name))
|
||||
continue;
|
||||
if (!lookup_symbol(table, rela->sym->name, &symbol))
|
||||
ERROR("can't find symbol '%s' in symbol table",
|
||||
rela->sym->name);
|
||||
|
||||
external = 0;
|
||||
/*
|
||||
* sym_objname is the name of the object to which
|
||||
* rela->sym belongs. We'll need this to build
|
||||
* ".klp.sym." symbol names later on.
|
||||
*
|
||||
* By default sym_objname is the name of the
|
||||
* component being patched (vmlinux or module).
|
||||
* If it's an external symbol, sym_objname
|
||||
* will get reassigned appropriately.
|
||||
*/
|
||||
sym_objname = objname;
|
||||
|
||||
/*
|
||||
* On ppc64le, the function prologue generated by GCC 6
|
||||
* has the sequence:
|
||||
*
|
||||
* .globl my_func
|
||||
* .type my_func, @function
|
||||
* .quad .TOC.-my_func
|
||||
* my_func:
|
||||
* .reloc ., R_PPC64_ENTRY ; optional
|
||||
* ld r2,-8(r12)
|
||||
* add r2,r2,r12
|
||||
* .localentry my_func, .-my_func
|
||||
*
|
||||
* The R_PPC64_ENTRY is optional and its symbol might
|
||||
* have an empty name. Leave it as a normal rela.
|
||||
*/
|
||||
if (rela->type == R_PPC64_ENTRY)
|
||||
continue;
|
||||
|
||||
if (rela->sym->bind == STB_LOCAL) {
|
||||
/* An unchanged local symbol */
|
||||
if (!lookup_local_symbol(table, rela->sym->name,
|
||||
&symbol))
|
||||
ERROR("lookup_local_symbol %s needed for %s",
|
||||
rela->sym->name, sec->base->name);
|
||||
|
||||
}
|
||||
else if (vmlinux) {
|
||||
/*
|
||||
* We have a patch to vmlinux which references
|
||||
* a global symbol. Use a normal rela for
|
||||
* exported symbols and a dynrela otherwise.
|
||||
*/
|
||||
#ifdef __powerpc64__
|
||||
/*
|
||||
* An exported symbol might be local to an
|
||||
* object file and any access to the function
|
||||
* might be through localentry (toc+offset)
|
||||
* instead of global offset.
|
||||
*
|
||||
* fs/proc/proc_sysctl::sysctl_head_grab:
|
||||
* 166: 0000000000000000 256 FUNC GLOBAL DEFAULT [<localentry>: 8] 42 unregister_sysctl_table
|
||||
* 167: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND .TOC.
|
||||
*
|
||||
* These type of symbols have a type of
|
||||
* STT_FUNC. Treat them like local symbols.
|
||||
* They will be handled by the livepatch
|
||||
* relocation code.
|
||||
*/
|
||||
if (lookup_is_exported_symbol(table, rela->sym->name)) {
|
||||
if (rela->sym->type != STT_FUNC)
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (lookup_is_exported_symbol(table, rela->sym->name))
|
||||
continue;
|
||||
#endif
|
||||
/*
|
||||
* If lookup_global_symbol() fails, assume the
|
||||
* symbol is defined in another object in the
|
||||
* patch module.
|
||||
*/
|
||||
if (!lookup_global_symbol(table, rela->sym->name,
|
||||
&symbol))
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* We have a patch to a module which references
|
||||
* a global symbol. Try to find the symbol in
|
||||
* the module being patched.
|
||||
*/
|
||||
if (!lookup_global_symbol(table, rela->sym->name,
|
||||
&symbol)) {
|
||||
/*
|
||||
* Not there, see if the symbol is
|
||||
* exported, and set sym_objname to the
|
||||
* object the exported symbol belongs
|
||||
* to. If it's not exported, assume sym
|
||||
* is provided by another .o in the
|
||||
* patch module.
|
||||
*/
|
||||
sym_objname = lookup_exported_symbol_objname(table, rela->sym->name);
|
||||
if (!sym_objname)
|
||||
sym_objname = pmod_name;
|
||||
|
||||
/*
|
||||
* For a symbol exported by vmlinux, use
|
||||
* the original rela.
|
||||
*
|
||||
* For a symbol exported by a module,
|
||||
* convert to a dynrela because the
|
||||
* module might not be loaded yet.
|
||||
*/
|
||||
if (!strcmp(sym_objname, "vmlinux"))
|
||||
continue;
|
||||
|
||||
external = 1;
|
||||
}
|
||||
}
|
||||
log_debug("lookup for %s @ 0x%016lx len %lu\n",
|
||||
rela->sym->name, symbol.addr, symbol.size);
|
||||
log_debug("lookup for %s: obj=%s sympos=%lu",
|
||||
rela->sym->name, symbol.objname,
|
||||
symbol.sympos);
|
||||
|
||||
/* Fill in ksyms[index] */
|
||||
if (vmlinux)
|
||||
|
@ -3119,7 +3126,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
||||
rela2->sym = strsym;
|
||||
rela2->type = ABSOLUTE_RELA_TYPE;
|
||||
rela2->addend = offset_of_string(&kelf->strings, sym_objname);
|
||||
rela2->addend = offset_of_string(&kelf->strings, symbol.objname);
|
||||
rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \
|
||||
offsetof(struct kpatch_symbol, objname));
|
||||
|
||||
|
@ -3129,7 +3136,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
rela->addend -= rela->sym->sym.st_value;
|
||||
krelas[index].addend = rela->addend;
|
||||
krelas[index].type = rela->type;
|
||||
krelas[index].external = external;
|
||||
krelas[index].external = !vmlinux && symbol.exported;
|
||||
|
||||
/* add rela to fill in krelas[index].dest field */
|
||||
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
||||
|
|
|
@ -399,8 +399,8 @@ void lookup_close(struct lookup_table *table)
|
|||
free(table);
|
||||
}
|
||||
|
||||
bool lookup_local_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
static bool lookup_local_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
{
|
||||
struct object_symbol *sym;
|
||||
unsigned long sympos = 0;
|
||||
|
@ -434,14 +434,51 @@ bool lookup_local_symbol(struct lookup_table *table, char *name,
|
|||
result->addr = sym->addr;
|
||||
result->size = sym->size;
|
||||
result->sympos = sympos;
|
||||
result->global = false;
|
||||
result->exported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!result->objname;
|
||||
}
|
||||
|
||||
bool lookup_global_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
static bool lookup_exported_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
{
|
||||
struct export_symbol *sym;
|
||||
int i;
|
||||
|
||||
if (result)
|
||||
memset(result, 0, sizeof(*result));
|
||||
|
||||
for_each_exp_symbol(i, sym, table) {
|
||||
if (!strcmp(sym->name, name)) {
|
||||
|
||||
if (!result)
|
||||
return true;
|
||||
|
||||
if (result->objname)
|
||||
ERROR("duplicate exported symbol found for %s", name);
|
||||
|
||||
result->objname = sym->objname;
|
||||
result->addr = 0; /* determined at runtime */
|
||||
result->size = 0; /* not used for exported symbols */
|
||||
result->sympos = 0; /* always 0 for exported symbols */
|
||||
result->global = true;
|
||||
result->exported = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result && result->objname;
|
||||
}
|
||||
|
||||
bool is_exported(struct lookup_table *table, char *name)
|
||||
{
|
||||
return lookup_exported_symbol(table, name, NULL);
|
||||
}
|
||||
|
||||
static bool lookup_global_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
{
|
||||
struct object_symbol *sym;
|
||||
int i;
|
||||
|
@ -458,47 +495,22 @@ bool lookup_global_symbol(struct lookup_table *table, char *name,
|
|||
result->addr = sym->addr;
|
||||
result->size = sym->size;
|
||||
result->sympos = 0; /* always 0 for global symbols */
|
||||
result->global = true;
|
||||
result->exported = is_exported(table, name);
|
||||
}
|
||||
}
|
||||
|
||||
return !!result->objname;
|
||||
}
|
||||
|
||||
bool lookup_is_exported_symbol(struct lookup_table *table, char *name)
|
||||
bool lookup_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result)
|
||||
{
|
||||
struct export_symbol *sym, *match = NULL;
|
||||
int i;
|
||||
if (lookup_local_symbol(table, name, result))
|
||||
return true;
|
||||
|
||||
for_each_exp_symbol(i, sym, table) {
|
||||
if (!strcmp(sym->name, name)) {
|
||||
if (match)
|
||||
ERROR("duplicate exported symbol found for %s", name);
|
||||
match = sym;
|
||||
}
|
||||
}
|
||||
if (lookup_global_symbol(table, name, result))
|
||||
return true;
|
||||
|
||||
return !!match;
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup_exported_symbol_objname - find the object/module an exported
|
||||
* symbol belongs to.
|
||||
*/
|
||||
char *lookup_exported_symbol_objname(struct lookup_table *table, char *name)
|
||||
{
|
||||
struct export_symbol *sym, *match = NULL;
|
||||
int i;
|
||||
|
||||
for_each_exp_symbol(i, sym, table) {
|
||||
if (!strcmp(sym->name, name)) {
|
||||
if (match)
|
||||
ERROR("duplicate exported symbol found for %s", name);
|
||||
match = sym;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
return match->objname;
|
||||
|
||||
return NULL;
|
||||
return lookup_exported_symbol(table, name, result);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ struct lookup_result {
|
|||
unsigned long addr;
|
||||
unsigned long size;
|
||||
unsigned long sympos;
|
||||
bool global, exported;
|
||||
};
|
||||
|
||||
struct sym_compare_type {
|
||||
|
@ -21,11 +22,7 @@ struct lookup_table *lookup_open(char *symtab_path, char *objname,
|
|||
char *symvers_path, char *hint,
|
||||
struct sym_compare_type *locals);
|
||||
void lookup_close(struct lookup_table *table);
|
||||
bool lookup_local_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result);
|
||||
bool lookup_global_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result);
|
||||
bool lookup_is_exported_symbol(struct lookup_table *table, char *name);
|
||||
char *lookup_exported_symbol_objname(struct lookup_table *table, char *name);
|
||||
bool lookup_symbol(struct lookup_table *table, char *name,
|
||||
struct lookup_result *result);
|
||||
|
||||
#endif /* _LOOKUP_H_ */
|
||||
|
|
Loading…
Reference in New Issue