152 lines
5.7 KiB
C
152 lines
5.7 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ This program is free software; you can redistribute it and/or modify │
|
|
│ it under the terms of the GNU General Public License as published by │
|
|
│ the Free Software Foundation; version 2 of the License. │
|
|
│ │
|
|
│ This program is distributed in the hope that it will be useful, but │
|
|
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
|
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
|
│ General Public License for more details. │
|
|
│ │
|
|
│ You should have received a copy of the GNU General Public License │
|
|
│ along with this program; if not, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/alg/alg.h"
|
|
#include "libc/alg/arraylist2.h"
|
|
#include "libc/elf/elf.h"
|
|
#include "libc/elf/struct/sym.h"
|
|
#include "libc/log/check.h"
|
|
#include "tool/build/lib/dis.h"
|
|
|
|
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
|
|
if (a->addr != b->addr) {
|
|
if (a->addr < b->addr) return -1;
|
|
if (a->addr > b->addr) return +1;
|
|
}
|
|
if (a->rank != b->rank) {
|
|
if (a->rank > b->rank) return -1;
|
|
if (a->rank < b->rank) return +1;
|
|
}
|
|
if (a->unique != b->unique) {
|
|
if (a->unique < b->unique) return -1;
|
|
if (a->unique > b->unique) return +1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void DisLoadElfLoads(struct Dis *d, struct Elf *elf) {
|
|
long i;
|
|
int64_t addr;
|
|
uint64_t size;
|
|
Elf64_Phdr *phdr;
|
|
struct DisLoad l;
|
|
d->loads.i = 0;
|
|
for (i = 0; i < elf->ehdr->e_phnum; ++i) {
|
|
phdr = getelfsegmentheaderaddress(elf->ehdr, elf->size, i);
|
|
if (phdr->p_type != PT_LOAD) continue;
|
|
l.addr = phdr->p_vaddr;
|
|
l.size = phdr->p_memsz;
|
|
l.istext = (phdr->p_flags & PF_X) == PF_X;
|
|
APPEND(&d->loads.p, &d->loads.i, &d->loads.n, &l);
|
|
}
|
|
}
|
|
|
|
static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
|
size_t i, n;
|
|
int64_t stablen;
|
|
struct DisSym t;
|
|
const Elf64_Sym *st, *sym;
|
|
bool isabs, iscode, isweak, islocal, ishidden, isprotected, isfunc, isobject;
|
|
d->syms.i = 0;
|
|
if ((d->syms.stab = getelfstringtable(elf->ehdr, elf->size)) &&
|
|
(st = getelfsymboltable(elf->ehdr, elf->size, &n))) {
|
|
stablen = (intptr_t)elf->ehdr + elf->size - (intptr_t)d->syms.stab;
|
|
for (i = 0; i < n; ++i) {
|
|
if (!st[i].st_name) continue;
|
|
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
|
|
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
|
|
isabs = st[i].st_shndx == SHN_ABS;
|
|
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
|
|
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
|
|
ishidden = st[i].st_other == STV_HIDDEN;
|
|
isprotected = st[i].st_other == STV_PROTECTED;
|
|
isfunc = ELF64_ST_TYPE(st[i].st_info) == STT_FUNC;
|
|
isobject = ELF64_ST_TYPE(st[i].st_info) == STT_OBJECT;
|
|
t.unique = i;
|
|
t.size = st[i].st_size;
|
|
t.name = st[i].st_name;
|
|
t.addr = st[i].st_value;
|
|
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
|
|
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
|
|
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
|
|
}
|
|
}
|
|
qsort(d->syms.p, d->syms.i, sizeof(struct DisSym), (void *)DisSymCompare);
|
|
}
|
|
|
|
bool DisIsProg(struct Dis *d, int64_t addr) {
|
|
long i;
|
|
for (i = 0; i < d->loads.i; ++i) {
|
|
if (addr >= d->loads.p[i].addr &&
|
|
addr < d->loads.p[i].addr + d->loads.p[i].size) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DisIsText(struct Dis *d, int64_t addr) {
|
|
long i;
|
|
for (i = 0; i < d->loads.i; ++i) {
|
|
if (addr >= d->loads.p[i].addr &&
|
|
addr < d->loads.p[i].addr + d->loads.p[i].size) {
|
|
return d->loads.p[i].istext;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
long DisFindSym(struct Dis *d, int64_t addr) {
|
|
size_t i;
|
|
if (DisIsProg(d, addr)) {
|
|
for (i = 0; i < d->syms.i; ++i) {
|
|
if (addr == d->syms.p[i].addr) return i;
|
|
}
|
|
for (i = 0; i < d->syms.i; ++i) {
|
|
if (addr >= d->syms.p[i].addr &&
|
|
addr < d->syms.p[i].addr + d->syms.p[i].size) {
|
|
return i;
|
|
}
|
|
}
|
|
for (i = 0; i < d->syms.i; ++i) {
|
|
if (addr >= d->syms.p[i].addr &&
|
|
(i + 1 == d->syms.i || addr < d->syms.p[i + 1].addr)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
long DisFindSymByName(struct Dis *d, const char *s) {
|
|
long i;
|
|
for (i = 0; i < d->syms.i; ++i) {
|
|
if (strcmp(s, d->syms.stab + d->syms.p[i].name) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void DisLoadElf(struct Dis *d, struct Elf *elf) {
|
|
if (!elf || !elf->ehdr) return;
|
|
DisLoadElfLoads(d, elf);
|
|
DisLoadElfSyms(d, elf);
|
|
}
|