114 lines
4.3 KiB
C++
114 lines
4.3 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#define START "__dlwrap__start"
|
|
|
|
#include "crt_arch.h"
|
|
|
|
#include <elf.h>
|
|
#include <link.h>
|
|
#include "reloc.h"
|
|
|
|
#define AUX_CNT 32
|
|
|
|
#include "ldso_trampoline_phdr.h"
|
|
|
|
// The location of the embedded linker in the executable.
|
|
// Hidden visibility is used to get a pc-relative reference instead
|
|
// of a GOT reference, which isn't available when this code runs.
|
|
__attribute__((visibility("hidden"))) extern const char __dlwrap_linker;
|
|
__attribute__((visibility("hidden"))) extern const char __dlwrap_linker_end;
|
|
|
|
// The real entry point of the binary to use after linker bootstrapping.
|
|
__attribute__((visibility("hidden"))) extern "C" void _start();
|
|
|
|
// Allocate some R/W memory to store a copy of the program headers.
|
|
static ElfW(Phdr) phdr_copy[64];
|
|
|
|
static size_t get_auxv(size_t* auxv, size_t entry) {
|
|
for (size_t i = 0; auxv[i]; i += 2)
|
|
if (auxv[i] == entry) return auxv[i + 1];
|
|
return 0;
|
|
}
|
|
|
|
static void set_auxv(size_t* auxv, size_t entry, size_t value) {
|
|
for (size_t i = 0; auxv[i]; i += 2) {
|
|
if (auxv[i] == entry) {
|
|
auxv[i + 1] = value;
|
|
return;
|
|
}
|
|
}
|
|
__builtin_trap();
|
|
}
|
|
|
|
/*
|
|
* This is the entry point for the linker wrapper, which finds
|
|
* the real linker, then bootstraps into it.
|
|
*/
|
|
extern "C" void __dlwrap__start_c(size_t* sp) {
|
|
size_t i, aux[AUX_CNT];
|
|
|
|
int argc = *sp;
|
|
char** argv = reinterpret_cast<char**>(sp + 1);
|
|
|
|
for (i = argc + 1; argv[i]; i++)
|
|
;
|
|
size_t* auxv = reinterpret_cast<size_t*>(argv + i + 1);
|
|
|
|
for (i = 0; i < AUX_CNT; i++) aux[i] = 0;
|
|
for (i = 0; auxv[i]; i += 2)
|
|
if (auxv[i] < AUX_CNT) aux[auxv[i]] = auxv[i + 1];
|
|
|
|
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(get_auxv(auxv, AT_PHDR));
|
|
size_t phdr_count = get_auxv(auxv, AT_PHNUM);
|
|
ElfW(Addr) load_bias = get_elf_load_bias_from_phdr(phdr, phdr_count);
|
|
|
|
ElfW(Addr) linker_addr = reinterpret_cast<ElfW(Addr)>(&__dlwrap_linker);
|
|
ElfW(Addr) linker_size = static_cast<ElfW(Addr)>(&__dlwrap_linker_end - &__dlwrap_linker);
|
|
ElfW(Addr) linker_vaddr = linker_addr - load_bias;
|
|
ElfW(Addr) linker_entry_offset = reinterpret_cast<ElfW(Ehdr)*>(linker_addr)->e_entry;
|
|
|
|
// Make a copy of the ELF program headers that does not contain the load
|
|
// segments for the embedded linker. The embedded linker contains its
|
|
// own copy of its load segments, which causes problems if musl uses
|
|
// both sets of load segments when donating unused space to the heap.
|
|
if (phdr_count > sizeof(phdr_copy) / sizeof(phdr_copy[0])) __builtin_trap();
|
|
copy_phdr(phdr, phdr_copy, phdr_count, load_bias);
|
|
phdr_trim_embedded_linker(phdr_copy, phdr_count, linker_vaddr, linker_vaddr + linker_size);
|
|
|
|
// Set AT_BASE to the embedded linker
|
|
set_auxv(auxv, AT_BASE, linker_addr);
|
|
// Set AT_ENTRY to the proper entry point
|
|
set_auxv(auxv, AT_ENTRY, reinterpret_cast<ElfW(Addr)>(&_start));
|
|
// Set AT_PHDR to the copied program headers
|
|
set_auxv(auxv, AT_PHDR, reinterpret_cast<ElfW(Addr)>(&phdr_copy));
|
|
|
|
// Jump to linker entry point
|
|
CRTJMP(linker_addr + linker_entry_offset, sp);
|
|
}
|