📄 elf.c
字号:
target = (Elf_Addr)(defobj->relocbase + def->st_value);
dbg("\"%s\" in \"%s\" ==> %p in \"%s\"",
defobj->strtab + def->st_name, basename(obj->path),
(void *)target, basename(defobj->path));
/*
* Write the new contents for the jmpslot. Note that depending on
* architecture, the value which we need to return back to the
* lazy binding trampoline may or may not be the target
* address. The value returned from reloc_jmpslot() is the value
* that the trampoline needs.
*/
target = reloc_jmpslot(where, target, defobj, obj, rel);
rlock_release(rtld_bind_lock, lockstate);
return target;
}
#if 0
/*
* Error reporting function. Use it like printf. If formats the message
* into a buffer, and sets things up so that the next call to dlerror()
* will return the message.
*/
void
_rtld_error(const char *fmt, ...)
{
static char buf[512];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
error_message = buf;
va_end(ap);
}
/*
* Return a dynamically-allocated copy of the current error message, if any.
*/
static char *
errmsg_save(void)
{
return error_message == NULL ? NULL : xstrdup(error_message);
}
/*
* Restore the current error message from a copy which was previously saved
* by errmsg_save(). The copy is freed.
*/
static void
errmsg_restore(char *saved_msg)
{
if (saved_msg == NULL)
error_message = NULL;
else {
_rtld_error("%s", saved_msg);
free(saved_msg);
}
}
static const char *
basename(const char *name)
{
const char *p = strrchr(name, '/');
return p != NULL ? p + 1 : name;
}
static void
die(void)
{
const char *msg = dlerror();
if (msg == NULL)
msg = "Fatal error";
errx(1, "%s", msg);
}
#endif /* 0 */
/*
* Process a shared object's DYNAMIC section, and save the important
* information in its Obj_Entry structure.
*/
static void
digest_dynamic(Obj_Entry *obj, int early)
{
const Elf_Dyn *dynp;
Needed_Entry **needed_tail = &obj->needed;
const Elf_Dyn *dyn_rpath = NULL;
int plttype = DT_REL;
obj->bind_now = false;
for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) {
switch (dynp->d_tag) {
case DT_REL:
obj->rel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_RELSZ:
obj->relsize = dynp->d_un.d_val;
break;
case DT_RELENT:
assert(dynp->d_un.d_val == sizeof(Elf_Rel));
break;
case DT_JMPREL:
obj->pltrel = (const Elf_Rel *)
(obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_PLTRELSZ:
obj->pltrelsize = dynp->d_un.d_val;
break;
case DT_RELA:
obj->rela = (const Elf_Rela *) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_RELASZ:
obj->relasize = dynp->d_un.d_val;
break;
case DT_RELAENT:
assert(dynp->d_un.d_val == sizeof(Elf_Rela));
break;
case DT_PLTREL:
plttype = dynp->d_un.d_val;
assert(dynp->d_un.d_val == DT_REL || plttype == DT_RELA);
break;
case DT_SYMTAB:
obj->symtab = (const Elf_Sym *)
(obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_SYMENT:
assert(dynp->d_un.d_val == sizeof(Elf_Sym));
break;
case DT_STRTAB:
obj->strtab = (const char *) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_STRSZ:
obj->strsize = dynp->d_un.d_val;
break;
case DT_HASH:
{
const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
(obj->relocbase + dynp->d_un.d_ptr);
obj->nbuckets = hashtab[0];
obj->nchains = hashtab[1];
obj->buckets = hashtab + 2;
obj->chains = obj->buckets + obj->nbuckets;
}
break;
case DT_NEEDED:
if (!obj->rtld) {
Needed_Entry *nep = NEW(Needed_Entry);
nep->name = dynp->d_un.d_val;
nep->obj = NULL;
nep->next = NULL;
*needed_tail = nep;
needed_tail = &nep->next;
}
break;
case DT_PLTGOT:
obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_TEXTREL:
obj->textrel = true;
break;
case DT_SYMBOLIC:
obj->symbolic = true;
break;
case DT_RPATH:
case DT_RUNPATH: /* XXX: process separately */
/*
* We have to wait until later to process this, because we
* might not have gotten the address of the string table yet.
*/
dyn_rpath = dynp;
break;
case DT_SONAME:
/* Not used by the dynamic linker. */
break;
case DT_INIT:
obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_FINI:
obj->fini = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr);
break;
case DT_DEBUG:
/* XXX - not implemented yet */
if (!early)
dbg("Filling in DT_DEBUG entry");
((Elf_Dyn*)dynp)->d_un.d_ptr = (Elf_Addr) &r_debug;
break;
case DT_FLAGS:
if (dynp->d_un.d_val & DF_ORIGIN) {
obj->origin_path = xmalloc(PATH_MAX);
if (rtld_dirname(obj->path, obj->origin_path) == -1)
die();
}
if (dynp->d_un.d_val & DF_SYMBOLIC)
obj->symbolic = true;
if (dynp->d_un.d_val & DF_TEXTREL)
obj->textrel = true;
if (dynp->d_un.d_val & DF_BIND_NOW)
obj->bind_now = true;
if (dynp->d_un.d_val & DF_STATIC_TLS)
;
break;
default:
if (!early) {
dbg("Ignoring d_tag %ld = %#lx", (long)dynp->d_tag,
(long)dynp->d_tag);
}
break;
}
}
obj->traced = false;
if (plttype == DT_RELA) {
obj->pltrela = (const Elf_Rela *) obj->pltrel;
obj->pltrel = NULL;
obj->pltrelasize = obj->pltrelsize;
obj->pltrelsize = 0;
}
if (dyn_rpath != NULL)
obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
}
/*
* Process a shared object's program header. This is used only for the
* main program, when the kernel has already loaded the main program
* into memory before calling the dynamic linker. It creates and
* returns an Obj_Entry structure.
*/
static Obj_Entry *
digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
{
Obj_Entry *obj;
const Elf_Phdr *phlimit = phdr + phnum;
const Elf_Phdr *ph;
int nsegs = 0;
obj = obj_new();
for (ph = phdr; ph < phlimit; ph++) {
switch (ph->p_type) {
case PT_PHDR:
if ((const Elf_Phdr *)ph->p_vaddr != phdr) {
_rtld_error("%s: invalid PT_PHDR", path);
return NULL;
}
obj->phdr = (const Elf_Phdr *) ph->p_vaddr;
obj->phsize = ph->p_memsz;
break;
case PT_INTERP:
obj->interp = (const char *) ph->p_vaddr;
break;
case PT_LOAD:
if (nsegs == 0) { /* First load segment */
obj->vaddrbase = trunc_page(ph->p_vaddr);
obj->mapbase = (caddr_t) obj->vaddrbase;
obj->relocbase = obj->mapbase - obj->vaddrbase;
obj->textsize = round_page(ph->p_vaddr + ph->p_memsz) -
obj->vaddrbase;
} else { /* Last load segment */
obj->mapsize = round_page(ph->p_vaddr + ph->p_memsz) -
obj->vaddrbase;
}
nsegs++;
break;
case PT_DYNAMIC:
obj->dynamic = (const Elf_Dyn *) ph->p_vaddr;
break;
case PT_TLS:
obj->tlsindex = 1;
obj->tlssize = ph->p_memsz;
obj->tlsalign = ph->p_align;
obj->tlsinitsize = ph->p_filesz;
obj->tlsinit = (void*) ph->p_vaddr;
break;
}
}
if (nsegs < 1) {
_rtld_error("%s: too few PT_LOAD segments", path);
return NULL;
}
obj->entry = entry;
return obj;
}
#if 0
static Obj_Entry *
dlcheck(void *handle)
{
Obj_Entry *obj;
for (obj = obj_list; obj != NULL; obj = obj->next)
if (obj == (Obj_Entry *) handle)
break;
if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) {
_rtld_error("Invalid shared object handle %p", handle);
return NULL;
}
return obj;
}
/*
* If the given object is already in the donelist, return true. Otherwise
* add the object to the list and return false.
*/
static bool
donelist_check(DoneList *dlp, const Obj_Entry *obj)
{
unsigned int i;
for (i = 0; i < dlp->num_used; i++)
if (dlp->objs[i] == obj)
return true;
/*
* Our donelist allocation should always be sufficient. But if
* our threads locking isn't working properly, more shared objects
* could have been loaded since we allocated the list. That should
* never happen, but we'll handle it properly just in case it does.
*/
if (dlp->num_used < dlp->num_alloc)
dlp->objs[dlp->num_used++] = obj;
return false;
}
#endif /* 0 */
/*
* Hash function for symbol table lookup. Don't even think about changing
* this. It is specified by the System V ABI.
*/
unsigned long
elf_hash(const char *name)
{
const unsigned char *p = (const unsigned char *) name;
unsigned long h = 0;
unsigned long g;
while (*p != '\0') {
h = (h << 4) + *p++;
if ((g = h & 0xf0000000) != 0)
h ^= g >> 24;
h &= ~g;
}
return h;
}
#if 0
/*
* Find the library with the given name, and return its full pathname.
* The returned string is dynamically allocated. Generates an error
* message and returns NULL if the library cannot be found.
*
* If the second argument is non-NULL, then it refers to an already-
* loaded shared object, whose library search path will be searched.
*
* The search order is:
* LD_LIBRARY_PATH
* rpath in the referencing file
* ldconfig hints
* /lib:/usr/lib
*/
static char *
find_library(const char *xname, const Obj_Entry *refobj)
{
char *pathname;
char *name;
if (strchr(xname, '/') != NULL) { /* Hard coded pathname */
if (xname[0] != '/' && !trust) {
_rtld_error("Absolute pathname required for shared object \"%s\"",
xname);
return NULL;
}
return xstrdup(xname);
}
if (libmap_disable || (refobj == NULL) ||
(name = lm_find(refobj->path, xname)) == NULL)
name = (char *)xname;
dbg(" Searching for \"%s\"", name);
if ((pathname = search_library_path(name, ld_library_path)) != NULL ||
(refobj != NULL &&
(pathname = search_library_path(name, refobj->rpath)) != NULL) ||
(pathname = search_library_path(name, gethints())) != NULL ||
(pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)
return pathname;
if(refobj != NULL && refobj->path != NULL) {
_rtld_error("Shared object \"%s\" not found, required by \"%s\"",
name, basename(refobj->path));
} else {
_rtld_error("Shared object \"%s\" not found", name);
}
return NULL;
}
#endif /* 0 */
/*
* Given a symbol number in a referencing object, find the corresponding
* definition of the symbol. Returns a pointer to the symbol, or NULL if
* no definition was found. Returns a pointer to the Obj_Entry of the
* defining object via the reference parameter DEFOBJ_OUT.
*/
const Elf_Sym *
find_symdef(unsigned long symnum, const Obj_Entry *refobj,
const Obj_Entry **defobj_out, bool in_plt, SymCache *cache)
{
const Elf_Sym *ref;
const Elf_Sym *def;
const Obj_Entry *defobj;
const char *name;
unsigned long hash;
/*
* If we have already found this symbol, get the information from
* the cache.
*/
if (symnum >= refobj->nchains)
return NULL; /* Bad object */
if (cache != NULL && cache[symnum].sym != NULL) {
*defobj_out = cache[symnum].obj;
return cache[symnum].sym;
}
ref = refobj->symtab + symnum;
name = refobj->strtab + ref->st_name;
defobj = NULL;
/*
* We don't have to do a full scale lookup if the symbol is local.
* We know it will bind to the instance in this load module; to
* which we already have a pointer (ie ref). By not doing a lookup,
* we not only improve performance, but it also avoids unresolvable
* symbols when local symbols are not in the hash table. This has
* been seen with the ia64 toolchain.
*/
if (ELF_ST_BIND(ref->st_info) != STB_LOCAL) {
if (ELF_ST_TYPE(ref->st_info) == STT_SECTION) {
_rtld_error("%s: Bogus symbol table entry %lu", refobj->path,
symnum);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -