arm64 (aarch64)

Tested and working: nrv2b and nrv2e for ET_EXE on linux.
NOT TESTED: lzma, ET_DYN on linux; any Mach-O

	modified:   bele.h
	modified:   conf.h
	modified:   filter/ct.h
	modified:   filteri.cpp
	modified:   linker.cpp
	modified:   linker.h
	modified:   miniacc.h
	modified:   p_elf_enum.h
	modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
	modified:   packmast.cpp
	modified:   stub/Makefile
	modified:   stub/arm64-darwin.macho-entry.h
	modified:   stub/arm64-darwin.macho-fold.h
	new file:   stub/arm64-linux.elf-entry.h
	new file:   stub/arm64-linux.elf-fold.h
	new file:   stub/arm64-linux.shlib-init.h
	new file:   stub/src/arch/arm64/v8/Makefile.extra
	modified:   stub/src/arch/arm64/v8/lzma_d-arm.S
	modified:   stub/src/arch/arm64/v8/lzma_d.S
	modified:   stub/src/arch/arm64/v8/lzma_d_cf.S
	new file:   stub/src/arch/arm64/v8/lzma_d_cs.S
	modified:   stub/src/arch/arm64/v8/macros.S
	renamed:    stub/src/arch/arm64/v8/nrv2b_d8.S -> stub/src/arch/arm64/v8/nrv2b_d32.S
	renamed:    stub/src/arch/arm64/v8/nrv2d_d8.S -> stub/src/arch/arm64/v8/nrv2d_d32.S
	modified:   stub/src/arch/arm64/v8/nrv2e_d32.S
	deleted:    stub/src/arch/arm64/v8/nrv2e_d8.S
	modified:   stub/src/arm64-darwin.macho-entry.S
	modified:   stub/src/arm64-darwin.macho-fold.S
	new file:   stub/src/arm64-linux.elf-entry.S
	new file:   stub/src/arm64-linux.elf-fold.S
	new file:   stub/src/arm64-linux.elf-fold.lds
	new file:   stub/src/arm64-linux.elf-main.c
	new file:   stub/src/arm64-linux.shlib-init.S
	modified:   stub/src/include/linux.h
	modified:   stub/tmp/arm64-darwin.macho-entry.bin.dump
	modified:   stub/tmp/arm64-darwin.macho-fold.map
	new file:   stub/tmp/arm64-linux.elf-entry.bin.dump
	new file:   stub/tmp/arm64-linux.elf-fold.map
	new file:   stub/tmp/arm64-linux.shlib-init.bin.dump
This commit is contained in:
John Reiser
2017-03-26 13:10:26 -07:00
parent 0e154cc759
commit b818e2c4b8
40 changed files with 4689 additions and 1780 deletions
+296
View File
@@ -650,6 +650,11 @@ Linker* PackLinuxElf64amd::newLinker() const
return new ElfLinkerAMD64;
}
Linker* PackLinuxElf64arm::newLinker() const
{
return new ElfLinkerARM64;
}
int const *
PackLinuxElf::getCompressionMethods(int method, int level) const
{
@@ -696,6 +701,15 @@ PackLinuxElf64amd::getFilters() const
return filters;
}
int const *
PackLinuxElf64arm::getFilters() const
{
static const int filters[] = {
0x52,
FT_END };
return filters;
}
void PackLinuxElf32::patchLoader()
{
}
@@ -740,6 +754,7 @@ void PackLinuxElf32::updateLoader(OutputFile * /*fo*/)
void PackLinuxElf64::updateLoader(OutputFile * /*fo*/)
{
set_te64(&elfout.ehdr.e_entry, sz_pack2 +
linker->getSymbolOffset("_start") +
get_te64(&elfout.phdr[0].p_vaddr));
}
@@ -788,10 +803,23 @@ PackLinuxElf64amd::PackLinuxElf64amd(InputFile *f)
ei_osabi = Elf32_Ehdr::ELFOSABI_LINUX;
}
PackLinuxElf64arm::PackLinuxElf64arm(InputFile *f)
: super(f)
{
e_machine = Elf64_Ehdr::EM_AARCH64;
ei_class = Elf64_Ehdr::ELFCLASS64;
ei_data = Elf64_Ehdr::ELFDATA2LSB;
ei_osabi = Elf32_Ehdr::ELFOSABI_LINUX;
}
PackLinuxElf64amd::~PackLinuxElf64amd()
{
}
PackLinuxElf64arm::~PackLinuxElf64arm()
{
}
static unsigned
umax(unsigned a, unsigned b)
{
@@ -1052,6 +1080,7 @@ PackLinuxElf64amd::defineSymbols(Filter const *)
len += (7&-lsize) + lsize;
is_big = (lo_va_user < (lo_va_stub + len + 2*page_size));
if (is_big && ehdri.ET_EXEC==get_te16(&ehdri.e_type)) {
// .e_entry is set later by PackLinuxElf64::updateLoader
set_te64( &elfout.ehdr.e_entry,
get_te64(&elfout.ehdr.e_entry) + lo_va_user - lo_va_stub);
set_te64(&elfout.phdr[0].p_vaddr, lo_va_user);
@@ -1347,6 +1376,28 @@ PackLinuxElf64amd::buildLoader(const Filter *ft)
stub_amd64_linux_elf_fold, sizeof(stub_amd64_linux_elf_fold), ft);
}
static const
#include "stub/arm64-linux.elf-entry.h"
static const
#include "stub/arm64-linux.elf-fold.h"
//static const
//#include "stub/arm64-linux.shlib-init.h"
void
PackLinuxElf64arm::buildLoader(const Filter *ft)
{
if (0!=xct_off) { // shared library
abort(); // FIXME
//buildLinuxLoader(
// stub_arm64_linux_shlib_init, sizeof(stub_arm64_linux_shlib_init),
// NULL, 0, ft );
//return;
}
buildLinuxLoader(
stub_arm64_linux_elf_entry, sizeof(stub_arm64_linux_elf_entry),
stub_arm64_linux_elf_fold, sizeof(stub_arm64_linux_elf_fold), ft);
}
Elf32_Shdr const *PackLinuxElf32::elf_find_section_name(
char const *const name
) const
@@ -2005,6 +2056,198 @@ proceed: ;
return true;
}
bool
PackLinuxElf64arm::canPack()
{
union {
unsigned char buf[sizeof(Elf64_Ehdr) + 14*sizeof(Elf64_Phdr)];
//struct { Elf64_Ehdr ehdr; Elf64_Phdr phdr; } e;
} u;
COMPILE_TIME_ASSERT(sizeof(u) <= 1024)
fi->readx(u.buf, sizeof(u.buf));
fi->seek(0, SEEK_SET);
Elf64_Ehdr const *const ehdr = (Elf64_Ehdr *) u.buf;
// now check the ELF header
if (checkEhdr(ehdr) != 0)
return false;
// additional requirements for linux/elf386
if (get_te16(&ehdr->e_ehsize) != sizeof(*ehdr)) {
throwCantPack("invalid Ehdr e_ehsize; try '--force-execve'");
return false;
}
if (e_phoff != sizeof(*ehdr)) {// Phdrs not contiguous with Ehdr
throwCantPack("non-contiguous Ehdr/Phdr; try '--force-execve'");
return false;
}
// The first PT_LOAD64 must cover the beginning of the file (0==p_offset).
Elf64_Phdr const *phdr = phdri;
for (unsigned j=0; j < e_phnum; ++phdr, ++j) {
if (j >= 14)
return false;
if (phdr->PT_LOAD64 == get_te32(&phdr->p_type)) {
load_va = get_te64(&phdr->p_vaddr);
upx_uint64_t file_offset = get_te64(&phdr->p_offset);
if (~page_mask & file_offset) {
if ((~page_mask & load_va) == file_offset) {
throwCantPack("Go-language PT_LOAD: try hemfix.c, or try '--force-execve'");
// Fixing it inside upx fails because packExtent() reads original file.
}
else {
throwCantPack("invalid Phdr p_offset; try '--force-execve'");
}
return false;
}
exetype = 1;
break;
}
}
// We want to compress position-independent executable (gcc -pie)
// main programs, but compressing a shared library must be avoided
// because the result is no longer usable. In theory, there is no way
// to tell them apart: both are just ET_DYN. Also in theory,
// neither the presence nor the absence of any particular symbol name
// can be used to tell them apart; there are counterexamples.
// However, we will use the following heuristic suggested by
// Peter S. Mazinger <ps.m@gmx.net> September 2005:
// If a ET_DYN has __libc_start_main as a global undefined symbol,
// then the file is a position-independent executable main program
// (that depends on libc.so.6) and is eligible to be compressed.
// Otherwise (no __libc_start_main as global undefined): skip it.
// Also allow __uClibc_main and __uClibc_start_main .
if (Elf32_Ehdr::ET_DYN==get_te16(&ehdr->e_type)) {
// The DT_STRTAB has no designated length. Read the whole file.
alloc_file_image(file_image, file_size);
fi->seek(0, SEEK_SET);
fi->readx(file_image, file_size);
memcpy(&ehdri, ehdr, sizeof(Elf64_Ehdr));
phdri= (Elf64_Phdr *)((size_t)e_phoff + file_image); // do not free() !!
shdri= (Elf64_Shdr const *)((size_t)e_shoff + file_image); // do not free() !!
//sec_strndx = &shdri[ehdr->e_shstrndx];
//shstrtab = (char const *)(sec_strndx->sh_offset + file_image);
sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM);
if (sec_dynsym)
sec_dynstr = get_te64(&sec_dynsym->sh_link) + shdri;
int j= e_phnum;
phdr= phdri;
for (; --j>=0; ++phdr)
if (Elf64_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
dynseg= (Elf64_Dyn const *)(get_te64(&phdr->p_offset) + file_image);
break;
}
// elf_find_dynamic() returns 0 if 0==dynseg.
dynstr= (char const *)elf_find_dynamic(Elf64_Dyn::DT_STRTAB);
dynsym= (Elf64_Sym const *)elf_find_dynamic(Elf64_Dyn::DT_SYMTAB);
// Modified 2009-10-10 to detect a ProgramLinkageTable relocation
// which references the symbol, because DT_GNU_HASH contains only
// defined symbols, and there might be no DT_HASH.
Elf64_Rela const *
rela= (Elf64_Rela const *)elf_find_dynamic(Elf64_Dyn::DT_RELA);
Elf64_Rela const *
jmprela= (Elf64_Rela const *)elf_find_dynamic(Elf64_Dyn::DT_JMPREL);
for ( int sz = elf_unsigned_dynamic(Elf64_Dyn::DT_PLTRELSZ);
0 < sz;
(sz -= sizeof(Elf64_Rela)), ++jmprela
) {
unsigned const symnum = get_te64(&jmprela->r_info) >> 32;
char const *const symnam = get_te32(&dynsym[symnum].st_name) + dynstr;
if (0==strcmp(symnam, "__libc_start_main")
|| 0==strcmp(symnam, "__uClibc_main")
|| 0==strcmp(symnam, "__uClibc_start_main"))
goto proceed;
}
// 2016-10-09 DT_JMPREL is no more (binutils-2.26.1)?
// Check the general case, too.
for ( int sz = elf_unsigned_dynamic(Elf64_Dyn::DT_RELASZ);
0 < sz;
(sz -= sizeof(Elf64_Rela)), ++rela
) {
unsigned const symnum = get_te64(&rela->r_info) >> 32;
char const *const symnam = get_te32(&dynsym[symnum].st_name) + dynstr;
if (0==strcmp(symnam, "__libc_start_main")
|| 0==strcmp(symnam, "__uClibc_main")
|| 0==strcmp(symnam, "__uClibc_start_main"))
goto proceed;
}
// Heuristic HACK for shared libraries (compare Darwin (MacOS) Dylib.)
// If there is an existing DT_INIT, and if everything that the dynamic
// linker ld-linux needs to perform relocations before calling DT_INIT
// resides below the first SHT_EXECINSTR Section in one PT_LOAD, then
// compress from the first executable Section to the end of that PT_LOAD.
// We must not alter anything that ld-linux might touch before it calls
// the DT_INIT function.
//
// Obviously this hack requires that the linker script put pieces
// into good positions when building the original shared library,
// and also requires ld-linux to behave.
if (elf_find_dynamic(Elf64_Dyn::DT_INIT)) {
if (elf_has_dynamic(Elf64_Dyn::DT_TEXTREL)) {
throwCantPack("DT_TEXTREL found; re-compile with -fPIC");
goto abandon;
}
Elf64_Shdr const *shdr = shdri;
xct_va = ~0ull;
for (j= e_shnum; --j>=0; ++shdr) {
if (Elf64_Shdr::SHF_EXECINSTR & get_te32(&shdr->sh_flags)) {
xct_va = umin64(xct_va, get_te64(&shdr->sh_addr));
}
}
// Rely on 0==elf_unsigned_dynamic(tag) if no such tag.
upx_uint64_t const va_gash = elf_unsigned_dynamic(Elf64_Dyn::DT_GNU_HASH);
upx_uint64_t const va_hash = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);
if (xct_va < va_gash || (0==va_gash && xct_va < va_hash)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_STRTAB)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_SYMTAB)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_REL)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_RELA)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_JMPREL)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERDEF)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERSYM)
|| xct_va < elf_unsigned_dynamic(Elf64_Dyn::DT_VERNEEDED) ) {
throwCantPack("DT_ tag above stub");
goto abandon;
}
for ((shdr= shdri), (j= e_shnum); --j>=0; ++shdr) {
upx_uint64_t const sh_addr = get_te64(&shdr->sh_addr);
if ( sh_addr==va_gash
|| (sh_addr==va_hash && 0==va_gash) ) {
shdr= &shdri[get_te32(&shdr->sh_link)]; // the associated SHT_SYMTAB
hatch_off = (char *)&ehdri.e_ident[11] - (char *)&ehdri;
break;
}
}
ACC_UNUSED(shdr);
xct_off = elf_get_offset_from_address(xct_va);
goto proceed; // But proper packing depends on checking xct_va.
}
abandon:
return false;
proceed: ;
}
// XXX Theoretically the following test should be first,
// but PackUnix::canPack() wants 0!=exetype ?
if (!super::canPack())
return false;
assert(exetype == 1);
exetype = 0;
// set options
opt->o_unix.blocksize = blocksize = file_size;
return true;
}
off_t
PackLinuxElf32::getbrk(const Elf32_Phdr *phdr, int nph) const
{
@@ -2675,6 +2918,14 @@ void PackLinuxElf64amd::pack1(OutputFile *fo, Filter &ft)
generateElfHdr(fo, stub_amd64_linux_elf_fold, getbrk(phdri, e_phnum) );
}
void PackLinuxElf64arm::pack1(OutputFile *fo, Filter &ft)
{
super::pack1(fo, ft);
if (0!=xct_off) // shared library
return;
generateElfHdr(fo, stub_arm64_linux_elf_fold, getbrk(phdri, e_phnum) );
}
// Determine length of gap between PT_LOAD phdr[k] and closest PT_LOAD
// which follows in the file (or end-of-file). Optimize for common case
// where the PT_LOAD are adjacent ascending by .p_offset. Assume no overlap.
@@ -3013,6 +3264,51 @@ void PackLinuxElf32armBe::defineSymbols(Filter const *ft)
ARM_defineSymbols(ft);
}
void PackLinuxElf64arm::defineSymbols(Filter const * /*ft*/)
{
lsize = /*getLoaderSize()*/ 4 * 1024; // upper bound; avoid circularity
upx_uint64_t lo_va_user = ~0ul; // infinity
for (int j= e_phnum; --j>=0; ) {
if (PT_LOAD64 == get_te32(&phdri[j].p_type)) {
upx_uint64_t const va = get_te64(&phdri[j].p_vaddr);
if (va < lo_va_user) {
lo_va_user = va;
}
}
}
upx_uint64_t lo_va_stub = get_te64(&elfout.phdr[0].p_vaddr);
upx_uint64_t adrc = 0; // init: pacify c++-analyzer
upx_uint64_t adrm = 0; // init: pacify c++-analyzer
is_big = true; // kernel disallows mapping below 0x8000.
if (is_big) {
set_te64( &elfout.ehdr.e_entry, linker->getSymbolOffset("_start") +
get_te64(&elfout.ehdr.e_entry) + lo_va_user - lo_va_stub);
set_te64(&elfout.phdr[0].p_vaddr, lo_va_user);
set_te64(&elfout.phdr[0].p_paddr, lo_va_user);
lo_va_stub = lo_va_user;
adrc = lo_va_stub;
adrm = getbrk(phdri, e_phnum);
}
adrc = page_mask & (~page_mask + adrc); // round up to page boundary
adrm = page_mask & (~page_mask + adrm); // round up to page boundary
adrm += page_size; // Try: hole so that kernel does not extend the brk(0)
linker->defineSymbol("ADRM", adrm); // addr for map
linker->defineSymbol("CPR0", 4+ linker->getSymbolOffset("cpr0"));
linker->defineSymbol("LENF", 4+ linker->getSymbolOffset("end_decompress"));
ACC_UNUSED(adrc);
#define MAP_PRIVATE 2 /* UNIX standard */
#define MAP_FIXED 0x10 /* UNIX standard */
#define MAP_ANONYMOUS 0x20 /* UNIX standard */
#define MAP_PRIVANON 3 /* QNX anonymous private memory */
unsigned mflg = MAP_PRIVATE | MAP_ANONYMOUS;
//if (ARM_is_QNX())
// mflg = MAP_PRIVANON;
linker->defineSymbol("MFLG", mflg);
}
void PackLinuxElf32mipseb::defineSymbols(Filter const * /*ft*/)
{
unsigned const hlen = sz_elf_hdrs + sizeof(l_info) + sizeof(p_info);