|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
Copyright(C) 2016, Red Hat, Inc., Stanislav Kozina
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
80a84b |
it under the terms of the GNU General Public License as published by
|
|
Packit Service |
80a84b |
the Free Software Foundation, either version 3 of the License, or
|
|
Packit Service |
80a84b |
(at your option) any later version.
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
This program is distributed in the hope that it will be useful,
|
|
Packit Service |
80a84b |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
80a84b |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
80a84b |
GNU General Public License for more details.
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
You should have received a copy of the GNU General Public License
|
|
Packit Service |
80a84b |
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* This file contains the code which reads the __ksymtab section of the kernel
|
|
Packit Service |
80a84b |
* binaries to ensure that the symbol we parse is actually exported using the
|
|
Packit Service |
80a84b |
* EXPORT_SYMBOL() macro.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
#include <stdlib.h>
|
|
Packit Service |
80a84b |
#include <unistd.h>
|
|
Packit Service |
80a84b |
#include <stdio.h>
|
|
Packit Service |
80a84b |
#include <sys/types.h>
|
|
Packit Service |
80a84b |
#include <sys/stat.h>
|
|
Packit Service |
80a84b |
#include <stdbool.h>
|
|
Packit Service |
80a84b |
#include <fcntl.h>
|
|
Packit Service |
80a84b |
#include <string.h>
|
|
Packit Service |
80a84b |
#include <errno.h>
|
|
Packit Service |
80a84b |
#include <assert.h>
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
#include <libelf.h>
|
|
Packit Service |
80a84b |
#include <gelf.h>
|
|
Packit Service |
80a84b |
#include "main.h"
|
|
Packit Service |
80a84b |
#include "utils.h"
|
|
Packit Service |
80a84b |
#include "hash.h"
|
|
Packit Service |
80a84b |
#include "ksymtab.h"
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
#define KSYMTAB_STRINGS "__ksymtab_strings"
|
|
Packit Service |
80a84b |
#define SYMTAB ".symtab"
|
|
Packit Service |
80a84b |
#define STRTAB ".strtab"
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
#define KSYMTAB_SIZE 8192
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksymtab {
|
|
Packit Service |
80a84b |
struct hash *hash;
|
|
Packit Service |
80a84b |
size_t mark_count;
|
|
Packit Service |
80a84b |
};
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksym;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static int elf_get_section(Elf *elf,
|
|
Packit Service |
80a84b |
size_t shstrndx,
|
|
Packit Service |
80a84b |
const char *section,
|
|
Packit Service |
80a84b |
const char **d_data,
|
|
Packit Service |
80a84b |
size_t *size)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
Elf_Scn *scn;
|
|
Packit Service |
80a84b |
GElf_Shdr shdr;
|
|
Packit Service |
80a84b |
char *name;
|
|
Packit Service |
80a84b |
Elf_Data *data;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
scn = elf_nextscn(elf, NULL);
|
|
Packit Service |
80a84b |
for (; scn != NULL; scn = elf_nextscn(elf, scn)) {
|
|
Packit Service |
80a84b |
if (gelf_getshdr(scn, &shdr) != &shdr)
|
|
Packit Service |
80a84b |
fail("getshdr() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
name = elf_strptr(elf, shstrndx, shdr.sh_name);
|
|
Packit Service |
80a84b |
if (name == NULL)
|
|
Packit Service |
80a84b |
fail("elf_strptr() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (strcmp(name, section) == 0)
|
|
Packit Service |
80a84b |
break;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (scn == NULL) /* no suitable section */
|
|
Packit Service |
80a84b |
return -1;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* This is unlucky. Fedora/EL builds -debuginfo packages by running
|
|
Packit Service |
80a84b |
* eu-strip --reloc-debug-sections which places only standard .debug*
|
|
Packit Service |
80a84b |
* sections into the -debuginfo modules. The sections which cannot
|
|
Packit Service |
80a84b |
* be stripped completely (because they are allocated) are changed
|
|
Packit Service |
80a84b |
* to SHT_NOBITS type to indicate you need to look in the original
|
|
Packit Service |
80a84b |
* (non-debug) module for them. But those are xzipped.
|
|
Packit Service |
80a84b |
* So we reject such stuff. We only support fresh output from the
|
|
Packit Service |
80a84b |
* kernel build.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
if (shdr.sh_type == SHT_NOBITS) {
|
|
Packit Service |
80a84b |
printf("The %s section has type SHT_NOBITS. Most likely you're "
|
|
Packit Service |
80a84b |
"running this tool on modules coming from kernel-debuginfo "
|
|
Packit Service |
80a84b |
"packages. They don't contain the %s section, you need to "
|
|
Packit Service |
80a84b |
"use the raw modules before they are stripped\n", section,
|
|
Packit Service |
80a84b |
section);
|
|
Packit Service |
80a84b |
exit(1);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (gelf_getshdr(scn, &shdr) != &shdr)
|
|
Packit Service |
80a84b |
fail("getshdr() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
data = elf_getdata(scn, NULL);
|
|
Packit Service |
80a84b |
if (data == NULL || data->d_size == 0)
|
|
Packit Service |
80a84b |
fail("%s section empty!\n", section);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
*d_data = data->d_buf;
|
|
Packit Service |
80a84b |
*size = data->d_size;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return 0;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct elf_data *elf_open(const char *filename)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
Elf *elf;
|
|
Packit Service |
80a84b |
int fd;
|
|
Packit Service |
80a84b |
int class;
|
|
Packit Service |
80a84b |
GElf_Ehdr *ehdr;
|
|
Packit Service |
80a84b |
size_t shstrndx;
|
|
Packit Service |
80a84b |
struct elf_data *data = NULL;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_version(EV_CURRENT) == EV_NONE)
|
|
Packit Service |
80a84b |
fail("elf_version() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
fd = open(filename, O_RDONLY, 0);
|
|
Packit Service |
80a84b |
if (fd < 0)
|
|
Packit Service |
80a84b |
fail("Failed to open file %s: %s\n", filename,
|
|
Packit Service |
80a84b |
strerror(errno));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
elf = elf_begin(fd, ELF_C_READ, NULL);
|
|
Packit Service |
80a84b |
if (elf == NULL)
|
|
Packit Service |
80a84b |
fail("elf_begin() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_kind(elf) != ELF_K_ELF) {
|
|
Packit Service |
80a84b |
printf("Doesn't look like an ELF file, ignoring: %s\n",
|
|
Packit Service |
80a84b |
filename);
|
|
Packit Service |
80a84b |
(void) elf_end(elf);
|
|
Packit Service |
80a84b |
(void) close(fd);
|
|
Packit Service |
80a84b |
goto out;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ehdr = safe_zmalloc(sizeof(*ehdr));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (gelf_getehdr(elf, ehdr) == NULL)
|
|
Packit Service |
80a84b |
fail("getehdr() failed: %s\n", elf_errmsg(-1));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
class = gelf_getclass(elf);
|
|
Packit Service |
80a84b |
if (class != ELFCLASS64) {
|
|
Packit Service |
80a84b |
printf("Unsupported elf class of %s: %d\n", filename, class);
|
|
Packit Service |
80a84b |
free(ehdr);
|
|
Packit Service |
80a84b |
(void) elf_end(elf);
|
|
Packit Service |
80a84b |
(void) close(fd);
|
|
Packit Service |
80a84b |
goto out;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* Get section index of the string table associated with the section
|
|
Packit Service |
80a84b |
* headers in the ELF file.
|
|
Packit Service |
80a84b |
* Required by elf_get_section calls.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
if (elf_getshdrstrndx(elf, &shstrndx) != 0)
|
|
Packit Service |
80a84b |
fail("elf_getshdrstrndx() failed: %s\n", elf_errmsg(-1))
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
data = safe_zmalloc(sizeof(*data));
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
data->fd = fd;
|
|
Packit Service |
80a84b |
data->elf = elf;
|
|
Packit Service |
80a84b |
data->ehdr = ehdr;
|
|
Packit Service |
80a84b |
data->shstrndx = shstrndx;
|
|
Packit Service |
80a84b |
out:
|
|
Packit Service |
80a84b |
return data;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
void elf_close(struct elf_data *ed)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
if (ed == NULL)
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
(void) elf_end(ed->elf);
|
|
Packit Service |
80a84b |
(void) close(ed->fd);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static void elf_for_each_global_sym(struct elf_data *ed,
|
|
Packit Service |
80a84b |
void (*fn)(const char *name,
|
|
Packit Service |
80a84b |
uint64_t value,
|
|
Packit Service |
80a84b |
int binding,
|
|
Packit Service |
80a84b |
void *ctx),
|
|
Packit Service |
80a84b |
void *ctx)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
const Elf64_Sym *end;
|
|
Packit Service |
80a84b |
Elf64_Sym *sym;
|
|
Packit Service |
80a84b |
int binding;
|
|
Packit Service |
80a84b |
const char *name;
|
|
Packit Service |
80a84b |
const char *data;
|
|
Packit Service |
80a84b |
size_t size;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_get_section(ed->elf, ed->shstrndx, SYMTAB, &data, &size) < 0)
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
sym = (Elf64_Sym *)data;
|
|
Packit Service |
80a84b |
end = (Elf64_Sym *)(data + size);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
sym++; /* skip first zero record */
|
|
Packit Service |
80a84b |
for (; sym < end; sym++) {
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
binding = ELF64_ST_BIND(sym->st_info);
|
|
Packit Service |
80a84b |
if (!(binding == STB_GLOBAL ||
|
|
Packit Service |
80a84b |
binding == STB_WEAK))
|
|
Packit Service |
80a84b |
continue;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (sym->st_name == 0)
|
|
Packit Service |
80a84b |
continue;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (sym->st_name > ed->strtab_size)
|
|
Packit Service |
80a84b |
fail("Symbol name index %d out of range %ld\n",
|
|
Packit Service |
80a84b |
sym->st_name, ed->strtab_size);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
name = ed->strtab + sym->st_name;
|
|
Packit Service |
80a84b |
if (name == NULL)
|
|
Packit Service |
80a84b |
fail("Could not find symbol name\n");
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
fn(name, sym->st_value, binding, ctx);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
void ksymtab_ksym_mark(struct ksym *ksym)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
if (!ksym->mark)
|
|
Packit Service |
80a84b |
ksym->ksymtab->mark_count++;
|
|
Packit Service |
80a84b |
ksym->mark = true;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static void ksymtab_ksym_free(void *arg)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct ksym *ksym = arg;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
free(ksym->link);
|
|
Packit Service |
80a84b |
free(ksym);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
void ksymtab_free(struct ksymtab *ksymtab)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct hash *h;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (ksymtab == NULL)
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
h = ksymtab->hash;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
hash_free(h);
|
|
Packit Service |
80a84b |
free(ksymtab);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksymtab *ksymtab_new(size_t size)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct hash *h;
|
|
Packit Service |
80a84b |
struct ksymtab *ksymtab;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
h = hash_new(size, ksymtab_ksym_free);
|
|
Packit Service |
80a84b |
assert(h != NULL);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksymtab = safe_zmalloc(sizeof(*ksymtab));
|
|
Packit Service |
80a84b |
ksymtab->hash = h;
|
|
Packit Service |
80a84b |
/* ksymtab->mark_count is zeroed by the allocator */
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return ksymtab;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksym *ksymtab_add_sym(struct ksymtab *ksymtab,
|
|
Packit Service |
80a84b |
const char *str,
|
|
Packit Service |
80a84b |
size_t len,
|
|
Packit Service |
80a84b |
uint64_t value)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct hash *h = ksymtab->hash;
|
|
Packit Service |
80a84b |
struct ksym *ksym;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksym = safe_zmalloc(sizeof(*ksym) + len + 1);
|
|
Packit Service |
80a84b |
memcpy(ksym->key, str, len);
|
|
Packit Service |
80a84b |
ksym->key[len] = '\0';
|
|
Packit Service |
80a84b |
ksym->value = value;
|
|
Packit Service |
80a84b |
ksym->ksymtab = ksymtab;
|
|
Packit Service |
80a84b |
/* ksym->link is zeroed by the allocator */
|
|
Packit Service |
80a84b |
hash_add(h, ksym->key, ksym);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return ksym;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksym *ksymtab_copy_sym(struct ksymtab *ksymtab, struct ksym *ksym)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
const char *name = ksymtab_ksym_get_name(ksym);
|
|
Packit Service |
80a84b |
uint64_t value = ksymtab_ksym_get_value(ksym);
|
|
Packit Service |
80a84b |
char *link = ksymtab_ksym_get_link(ksym);
|
|
Packit Service |
80a84b |
struct ksym *new;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
new = ksymtab_add_sym(ksymtab, name, strlen(name), value);
|
|
Packit Service |
80a84b |
ksymtab_ksym_set_link(new, link);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return new;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct ksym *ksymtab_find(struct ksymtab *ksymtab, const char *name)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct ksym *v;
|
|
Packit Service |
80a84b |
struct hash *h = ksymtab->hash;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (name == NULL)
|
|
Packit Service |
80a84b |
return NULL;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
v = hash_find(h, name);
|
|
Packit Service |
80a84b |
if (v == NULL)
|
|
Packit Service |
80a84b |
return NULL;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return v;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
size_t ksymtab_len(struct ksymtab *ksymtab)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct hash *h;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (ksymtab == NULL)
|
|
Packit Service |
80a84b |
return 0;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
h = ksymtab->hash;
|
|
Packit Service |
80a84b |
return hash_get_count(h);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
size_t ksymtab_mark_count(struct ksymtab *ksymtab)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
return ksymtab->mark_count;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
void ksymtab_for_each(struct ksymtab *ksymtab,
|
|
Packit Service |
80a84b |
void (*f)(struct ksym *, void *),
|
|
Packit Service |
80a84b |
void *ctx)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct hash *h;
|
|
Packit Service |
80a84b |
struct hash_iter iter;
|
|
Packit Service |
80a84b |
const void *v;
|
|
Packit Service |
80a84b |
struct ksym *vv;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (ksymtab == NULL)
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
h = ksymtab->hash;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
hash_iter_init(h, &iter);
|
|
Packit Service |
80a84b |
while (hash_iter_next(&iter, NULL, &v)) {
|
|
Packit Service |
80a84b |
vv = (struct ksym *)v;
|
|
Packit Service |
80a84b |
f(vv, ctx);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/* Parses raw content of __ksymtab_strings section to a ksymtab */
|
|
Packit Service |
80a84b |
static struct ksymtab *parse_ksymtab_strings(const char *d_buf, size_t d_size)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
char *p, *oldp;
|
|
Packit Service |
80a84b |
size_t size = 0;
|
|
Packit Service |
80a84b |
size_t i = 0;
|
|
Packit Service |
80a84b |
struct ksymtab *res;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
res = ksymtab_new(KSYMTAB_SIZE);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
p = oldp = (char *)d_buf;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/* Make sure we have the final '\0' */
|
|
Packit Service |
80a84b |
if (p[d_size - 1] != '\0')
|
|
Packit Service |
80a84b |
fail("Mallformed " KSYMTAB_STRINGS " section: %s\n", p);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
for (size = 0; size < d_size; size++, p++) {
|
|
Packit Service |
80a84b |
/* End of symbol? */
|
|
Packit Service |
80a84b |
if (*p == '\0') {
|
|
Packit Service |
80a84b |
size_t len = p - oldp;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/* Skip empty strings */
|
|
Packit Service |
80a84b |
if (len == 0) {
|
|
Packit Service |
80a84b |
oldp = p + 1;
|
|
Packit Service |
80a84b |
continue;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksymtab_add_sym(res, oldp, len, i);
|
|
Packit Service |
80a84b |
i++;
|
|
Packit Service |
80a84b |
oldp = p + 1;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return res;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* An entry for address -> symbol mapping.
|
|
Packit Service |
80a84b |
* The key will be "value".
|
|
Packit Service |
80a84b |
* We can use name pointer directly from the elf,
|
|
Packit Service |
80a84b |
* it will be freed later.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
struct map_entry {
|
|
Packit Service |
80a84b |
uint64_t value;
|
|
Packit Service |
80a84b |
const char *name;
|
|
Packit Service |
80a84b |
};
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static struct map_entry *map_entry_new(uint64_t value, const char *name)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct map_entry *res;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
res = safe_zmalloc(sizeof(*res));
|
|
Packit Service |
80a84b |
res->value = value;
|
|
Packit Service |
80a84b |
res->name = name;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return res;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct weak_filter_ctx {
|
|
Packit Service |
80a84b |
struct ksymtab *ksymtab;
|
|
Packit Service |
80a84b |
struct ksymtab *weaks;
|
|
Packit Service |
80a84b |
struct hash *map;
|
|
Packit Service |
80a84b |
};
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* Does two things in one pass on the symbol table:
|
|
Packit Service |
80a84b |
* 1) makes address -> symbol map for GLOBAL symbols;
|
|
Packit Service |
80a84b |
* 2) collecs subset of EXPORTed symbol, which have WEAK binding.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
static void weak_filter(const char *name, uint64_t value, int bind, void *_ctx)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct weak_filter_ctx *ctx = _ctx;
|
|
Packit Service |
80a84b |
struct map_entry *m;
|
|
Packit Service |
80a84b |
struct ksym *ksym;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (bind == STB_GLOBAL) {
|
|
Packit Service |
80a84b |
m = map_entry_new(value, name);
|
|
Packit Service |
80a84b |
hash_add_bin(ctx->map,
|
|
Packit Service |
80a84b |
(const char *)&m->value, sizeof(m->value), m);
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/* WEAK handling */
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksym = ksymtab_find(ctx->ksymtab, name);
|
|
Packit Service |
80a84b |
if (ksym == NULL)
|
|
Packit Service |
80a84b |
/* skip non-exported aliases */
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksymtab_add_sym(ctx->weaks, name, strlen(name), value);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
struct weak_to_alias_ctx {
|
|
Packit Service |
80a84b |
struct ksymtab *aliases;
|
|
Packit Service |
80a84b |
struct hash *map;
|
|
Packit Service |
80a84b |
};
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static void weak_to_alias(struct ksym *ksym, void *_ctx)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct weak_to_alias_ctx *ctx = _ctx;
|
|
Packit Service |
80a84b |
struct map_entry *m;
|
|
Packit Service |
80a84b |
uint64_t value = ksymtab_ksym_get_value(ksym);
|
|
Packit Service |
80a84b |
const char *name = ksymtab_ksym_get_name(ksym);
|
|
Packit Service |
80a84b |
struct ksym *alias;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
m = hash_find_bin(ctx->map, (const char *)&value, sizeof(value));
|
|
Packit Service |
80a84b |
if (m == NULL)
|
|
Packit Service |
80a84b |
/* there is no GLOBAL alias for the WEAK exported symbol */
|
|
Packit Service |
80a84b |
return;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
alias = ksymtab_add_sym(ctx->aliases, m->name, strlen(m->name), 0);
|
|
Packit Service |
80a84b |
ksymtab_ksym_set_link(alias, name);
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static struct ksymtab *ksymtab_weaks_to_aliases(struct ksymtab *weaks,
|
|
Packit Service |
80a84b |
struct hash *map)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct ksymtab *aliases;
|
|
Packit Service |
80a84b |
struct weak_to_alias_ctx ctx;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
aliases = ksymtab_new(KSYMTAB_SIZE);
|
|
Packit Service |
80a84b |
if (aliases == NULL)
|
|
Packit Service |
80a84b |
fail("Cannot create ksymtab\n");
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ctx.aliases = aliases;
|
|
Packit Service |
80a84b |
ctx.map = map;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ksymtab_for_each(weaks, weak_to_alias, &ctx;;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return aliases;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* Generate weak aliases for the symbols, found in the list of exported.
|
|
Packit Service |
80a84b |
* It will work correctly for one alias only.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
static struct ksymtab *ksymtab_find_aliases(struct ksymtab *ksymtab,
|
|
Packit Service |
80a84b |
struct elf_data *elf)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
struct ksymtab *aliases;
|
|
Packit Service |
80a84b |
struct ksymtab *weaks;
|
|
Packit Service |
80a84b |
struct hash *map; /* address to name mapping */
|
|
Packit Service |
80a84b |
struct weak_filter_ctx ctx;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
weaks = ksymtab_new(KSYMTAB_SIZE);
|
|
Packit Service |
80a84b |
if (weaks == NULL)
|
|
Packit Service |
80a84b |
fail("Cannot create weaks symtab\n");
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
map = hash_new(KSYMTAB_SIZE, free);
|
|
Packit Service |
80a84b |
if (map == NULL)
|
|
Packit Service |
80a84b |
fail("Cannot create address->symbol mapping hash\n");
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
ctx.ksymtab = ksymtab;
|
|
Packit Service |
80a84b |
ctx.weaks = weaks;
|
|
Packit Service |
80a84b |
ctx.map = map;
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* If there's a weak symbol on the whitelist,
|
|
Packit Service |
80a84b |
* we need to find the proper global
|
|
Packit Service |
80a84b |
* symbol to generate the type for it.
|
|
Packit Service |
80a84b |
*
|
|
Packit Service |
80a84b |
* It is done in two steps below:
|
|
Packit Service |
80a84b |
* 1) create address -> global symbol mapping and
|
|
Packit Service |
80a84b |
* suitable weak symbol list;
|
|
Packit Service |
80a84b |
* 2) for all weak symbols find its alias with the mapping.
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
elf_for_each_global_sym(elf, weak_filter, &ctx;;
|
|
Packit Service |
80a84b |
aliases = ksymtab_weaks_to_aliases(weaks, map);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
hash_free(map);
|
|
Packit Service |
80a84b |
ksymtab_free(weaks);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return aliases;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
int elf_get_endianness(struct elf_data *data, unsigned int *endianness)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
if (data->ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
|
|
Packit Service |
80a84b |
data->ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
|
|
Packit Service |
80a84b |
printf("Unsupported ELF endianness (EI_DATA) found: %d.\n",
|
|
Packit Service |
80a84b |
data->ehdr->e_ident[EI_DATA]);
|
|
Packit Service |
80a84b |
return 1;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
*endianness = data->ehdr->e_ident[EI_DATA];
|
|
Packit Service |
80a84b |
return 0;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
static inline int elf_get_strtab(struct elf_data *data)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
const char *strtab;
|
|
Packit Service |
80a84b |
size_t strtab_size;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_get_section(data->elf, data->shstrndx, STRTAB, &strtab,
|
|
Packit Service |
80a84b |
&strtab_size) < 0) {
|
|
Packit Service |
80a84b |
return 1;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
data->strtab = strtab;
|
|
Packit Service |
80a84b |
data->strtab_size = strtab_size;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return 0;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
/*
|
|
Packit Service |
80a84b |
* Build list of exported symbols, ie. read seciton __ksymtab_strings,
|
|
Packit Service |
80a84b |
* analyze symbol table and create table of aliases -- list of global symbols,
|
|
Packit Service |
80a84b |
* which have the same addresses, as weak symbols,
|
|
Packit Service |
80a84b |
* mentioned by __ksymtab_strings
|
|
Packit Service |
80a84b |
*/
|
|
Packit Service |
80a84b |
int elf_get_exported(struct elf_data *data, struct ksymtab **ksymtab,
|
|
Packit Service |
80a84b |
struct ksymtab **aliases)
|
|
Packit Service |
80a84b |
{
|
|
Packit Service |
80a84b |
const char *ksymtab_raw;
|
|
Packit Service |
80a84b |
size_t ksymtab_sz;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_get_strtab(data) > 0)
|
|
Packit Service |
80a84b |
return 1;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
if (elf_get_section(data->elf, data->shstrndx, KSYMTAB_STRINGS,
|
|
Packit Service |
80a84b |
&ksymtab_raw, &ksymtab_sz) < 0)
|
|
Packit Service |
80a84b |
return 1;
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
*ksymtab = parse_ksymtab_strings(ksymtab_raw, ksymtab_sz);
|
|
Packit Service |
80a84b |
*aliases = ksymtab_find_aliases(*ksymtab, data);
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
return 0;
|
|
Packit Service |
80a84b |
}
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
|
|
Packit Service |
80a84b |
|