Allow for running 32-bit apps in 32-bit processes

Use this by default instead of 32in64
This commit is contained in:
Lubos Dolezel 2017-03-27 10:40:23 +02:00
parent 7d908aa8bf
commit 70a1ac7510
6 changed files with 114 additions and 29 deletions

View File

@ -17,6 +17,7 @@ function(wrap_elf name elfname)
include_directories(${CMAKE_SOURCE_DIR}/src/dyld)
add_darling_library(${name} SHARED ${CMAKE_CURRENT_BINARY_DIR}/${name}.c)
target_link_libraries(${name} PRIVATE system)
make_fat(${name})
install(TARGETS ${name} DESTINATION libexec/darling/usr/lib/native)
endfunction(wrap_elf)

View File

@ -11,7 +11,7 @@ enable_language(C ASM)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Ttext-segment,0x400000 -Wl,-Tbss,0x410000 -Wl,-Tdata,0x420000")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Ttext-segment,0x400000 -Wl,-Tbss,0x410000 -Wl,-Tdata,0x420000")
add_definitions(-DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" -D_GNU_SOURCE -DMLDR_BUILD)
add_executable(darling darling.c)
@ -28,10 +28,18 @@ set(mldr_sources
add_executable(mldr ${mldr_sources})
target_link_libraries(mldr pthread dl)
add_executable(mldr32 ${mldr_sources})
target_link_libraries(mldr32 pthread dl)
set_target_properties(mldr32
PROPERTIES
COMPILE_FLAGS "-m32"
LINK_FLAGS "-m32"
)
add_executable(wrapgen wrapgen/wrapgen.cpp)
target_link_libraries(wrapgen dl)
install(TARGETS mldr DESTINATION libexec/darling/bin)
install(TARGETS mldr mldr32 DESTINATION libexec/darling/bin)
install(TARGETS darling DESTINATION bin
PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE

View File

@ -42,24 +42,34 @@ void gdb_notifier(enum dyld_image_mode mode, uint32_t infoCount, const struct dy
struct jump
{
#ifdef __x86_64__
uint16_t mov;
void* addr;
uint16_t jump;
#elif __i386__
uint8_t mov;
void* addr;
uint16_t jump;
#endif
} __attribute__ ((packed));
void setup_gdb_notifications(uint64_t slide, uint64_t addr)
void setup_gdb_notifications(uintptr_t slide, uintptr_t addr)
{
orig_dyld_all_image_infos = (struct dyld_all_image_infos*)(addr + slide);
// dyld will later rebase the address in notification,
// but at this point we must add slide manually.
struct jump* jump = (struct jump*)(((uint64_t)orig_dyld_all_image_infos->notification) + slide);
struct jump* jump = (struct jump*)(((uintptr_t)orig_dyld_all_image_infos->notification) + slide);
// Rewrite instructions in the notification function to redirect the call to us.
#ifdef __x86_64__
jump->mov = 0xb948; // movabs imm,%rcx
jump->addr = (void*) &dyld_notification_wrapper; // immediate for preceding movabs
jump->jump = 0xe1ff; // jmpq *%ecx
jump->jump = 0xe1ff; // jmpq *%rcx
#elif __i386__
jump->mov = 0xb9; // mov imm,%ecx
jump->addr = (void*) &dyld_notification_wrapper; // immediate for preceding mov
jump->jump = 0xe1ff; // jmpl *%ecx
#else
# error TODO: Unsupported platform
#endif

View File

@ -26,7 +26,7 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
extern "C" {
#endif
void setup_gdb_notifications(uint64_t slide, uint64_t addr);
void setup_gdb_notifications(uintptr_t slide, uintptr_t addr);
#ifdef __cplusplus
}

View File

@ -24,14 +24,14 @@
# error See above
#endif
void FUNCTION_NAME(int fd, uint64_t* entryPoint_out, uint64_t* mh_out)
void FUNCTION_NAME(int fd, uintptr_t* entryPoint_out, uintptr_t* mh_out)
{
struct MACH_HEADER_STRUCT header;
uint8_t* cmds;
uint64_t entryPoint = 0, entryPointDylinker = 0;
uintptr_t entryPoint = 0, entryPointDylinker = 0;
struct MACH_HEADER_STRUCT* mappedHeader = NULL;
uint64_t slide = 0;
uint64_t mmapSize = 0;
uintptr_t slide = 0;
uintptr_t mmapSize = 0;
bool pie = false;
uint32_t fat_offset;
@ -58,7 +58,7 @@ void FUNCTION_NAME(int fd, uint64_t* entryPoint_out, uint64_t* mh_out)
if ((header.filetype == MH_EXECUTE && header.flags & MH_PIE) || header.filetype == MH_DYLINKER)
{
uint64_t base = -1;
uintptr_t base = -1;
// Go through all SEGMENT_COMMAND commands to get the total continuous range required.
for (uint32_t i = 0, p = 0; i < header.ncmds; i++)
@ -80,8 +80,8 @@ void FUNCTION_NAME(int fd, uint64_t* entryPoint_out, uint64_t* mh_out)
p += seg->cmdsize;
}
slide = (uint64_t) mmap((void*) base, mmapSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_EXTRA, -1, 0);
if (slide == (uint64_t)MAP_FAILED)
slide = (uintptr_t) mmap((void*) base, mmapSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_EXTRA, -1, 0);
if (slide == (uintptr_t)MAP_FAILED)
{
fprintf(stderr, "Cannot mmap anonymous memory range: %s", strerror(errno));
exit(1);
@ -183,7 +183,7 @@ no_slide:
if (length > sizeof(path)-1)
{
fprintf(stderr, "Dynamic linker name too long: %lu\n", length);
fprintf(stderr, "Dynamic linker name too long: %zu\n", length);
exit(1);
}
@ -193,10 +193,10 @@ no_slide:
apply_root_path(path);
#ifdef GEN_64BIT
load(path, &entryPointDylinker, NULL, CPU_TYPE_X86_64);
load(path, &entryPointDylinker, NULL, CPU_TYPE_X86_64, NULL);
#endif
#ifdef GEN_32BIT
load(path, &entryPointDylinker, NULL, CPU_TYPE_X86);
load(path, &entryPointDylinker, NULL, CPU_TYPE_X86, NULL);
#endif
break;
@ -218,7 +218,7 @@ no_slide:
if (entryPoint_out != NULL)
*entryPoint_out = entryPointDylinker ? entryPointDylinker : entryPoint;
if (mh_out != NULL)
*mh_out = (uint64_t) mappedHeader;
*mh_out = (uintptr_t) mappedHeader;
}

View File

@ -37,6 +37,8 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
#include "commpage.h"
#include "32in64.h"
#define USE_32IN64 0
#ifndef PAGE_SIZE
# define PAGE_SIZE 4096
#endif
@ -56,21 +58,28 @@ static const char* dyld_path = INSTALL_PREFIX "/libexec/usr/lib/dyld";
//
// Additionally, mldr providers access to native platforms libdl.so APIs (ELF loader).
static void load64(int fd, uint64_t* entryPoint_out, uint64_t* mh_out);
static void load32(int fd, uint64_t* entryPoint_out, uint64_t* mh_out);
static void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type_t cpu);
#ifdef __x86_64__
static void load64(int fd, uintptr_t* entryPoint_out, uintptr_t* mh_out);
static void reexec32(char** argv);
#endif
static void load32(int fd, uintptr_t* entryPoint_out, uintptr_t* mh_out);
static void load(const char* path, uintptr_t* entryPoint_out, uintptr_t* mh_out, cpu_type_t cpu, char** argv);
static int native_prot(int prot);
static void apply_root_path(char* path);
char* elfcalls_make(void);
static char* apple0_make(const char* filepath);
#if USE_32IN64
static void* setup_stack32(void* stack, int argc, const char** argv, const char** envp, const char** apple, uint64_t mh);
static bool mode_32in64 = false;
#endif
static uint32_t stack_size = 0;
int main(int argc, char** argv, char** envp)
{
uint64_t entryPoint, mh;
uintptr_t entryPoint, mh;
void** sp;
int pushCount = 0;
const char* apple[3];
@ -91,6 +100,12 @@ int main(int argc, char** argv, char** envp)
else
strcpy(filename, argv[1]);
#ifdef __i386__
load(filename, &entryPoint, &mh, CPU_TYPE_X86, argv); // accept i386 only
#else
load(filename, &entryPoint, &mh, CPU_TYPE_ANY, argv);
#endif
p = argv[0];
// Update process name in ps output
for (int i = 1; i < argc; i++)
@ -104,9 +119,7 @@ int main(int argc, char** argv, char** envp)
memset(p, 0, envp[0]-p);
argv[--argc] = NULL;
load(filename, &entryPoint, &mh, CPU_TYPE_ANY);
#if defined(__i386__) || defined(__x86_64__)
#if __i386__ || __x86_64__
if (getenv("BREAK_AFTER_LOAD") != NULL)
__asm__("int3");
#endif
@ -128,7 +141,9 @@ int main(int argc, char** argv, char** envp)
apple[1] = elfcalls_make();
apple[2] = NULL;
#if USE_32IN64
if (!mode_32in64)
#endif
{
GETSP(sp);
sp--;
@ -149,6 +164,7 @@ int main(int argc, char** argv, char** envp)
JUMPX(pushCount, entryPoint);
}
#if USE_32IN64
else
{
uint32_t size = stack_size ? stack_size : 8*1024*1024;
@ -165,7 +181,7 @@ int main(int argc, char** argv, char** envp)
_64TO32_WITH_STACK(setup_stack32(sp, argc, (const char**) argv, (const char**) envp, apple, mh), entryPoint);
}
#endif
__builtin_unreachable();
}
@ -189,7 +205,8 @@ static size_t arraylen(const char** str)
return len;
}
void* setup_stack32(void* stack, int argc, const char** argv_in, const char** envp_in, const char** apple_in, uint64_t mh)
#if USE_32IN64
void* setup_stack32(void* stack, int argc, const char** argv_in, const char** envp_in, const char** apple_in, uintptr_t mh)
{
char **argv_new, **envp_new, **apple_new;
size_t size, apple_size;
@ -231,8 +248,9 @@ void* setup_stack32(void* stack, int argc, const char** argv_in, const char** en
return stack_pointers;
}
#endif
void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type_t cpu_desired)
void load(const char* path, uintptr_t* entryPoint_out, uintptr_t* mh_out, cpu_type_t cpu_desired, char** argv)
{
int fd;
uint32_t magic;
@ -244,7 +262,7 @@ void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type
exit(1);
}
// TODO: We need to read argv[1] and detect whether it's a 32 or 64-bit application.
// We need to read argv[1] and detect whether it's a 32 or 64-bit application.
// Then load the appropriate version of dyld from the fat file.
// In case the to-be-executed executable contains both, we prefer the 64-bit version,
// unless a special property has been passed to sys_posix_spawn() to force the 32-bit
@ -258,20 +276,30 @@ void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type
if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64)
{
#ifndef __i386__
if (mh_out)
commpage_setup(true);
lseek(fd, 0, SEEK_SET);
load64(fd, entryPoint_out, mh_out);
#else
abort();
#endif
}
else if (magic == MH_MAGIC || magic == MH_CIGAM)
{
#if !__x86_64__ || USE_32IN64
if (mh_out)
commpage_setup(false);
#if __x86_64__
mode_32in64 = true;
#endif
lseek(fd, 0, SEEK_SET);
load32(fd, entryPoint_out, mh_out);
#else
// Re-run self as mldr32
reexec32(argv);
#endif
}
else if (magic == FAT_MAGIC || magic == FAT_CIGAM)
{
@ -349,11 +377,24 @@ void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type
commpage_setup(best_arch.cputype & CPU_ARCH_ABI64);
if (best_arch.cputype & CPU_ARCH_ABI64)
{
#if !__i386__
load64(fd, entryPoint_out, mh_out);
#else
abort();
#endif
}
else
{
#if USE_32IN64 && __x86_64__
mode_32in64 = true;
#endif
#if !USE_32IN64 && __x86_64__
// Re-execute self as mldr32
reexec32(argv);
#else
load32(fd, entryPoint_out, mh_out);
#endif
}
}
else
@ -365,9 +406,11 @@ void load(const char* path, uint64_t* entryPoint_out, uint64_t* mh_out, cpu_type
close(fd);
}
#ifdef __x86_64__
#define GEN_64BIT
#include "loader.c"
#undef GEN_64BIT
#endif
#define GEN_32BIT
#include "loader.c"
@ -443,3 +486,26 @@ char* apple0_make(const char* filepath)
return apple0;
}
#ifdef __x86_64__
static void reexec32(char** argv)
{
char selfpath[1024];
ssize_t len;
len = readlink("/proc/self/exe", selfpath, sizeof(selfpath)-3);
if (len == -1)
{
perror("Cannot readlink /proc/self/exe");
abort();
}
selfpath[len] = '\0';
strcat(selfpath, "32");
execv(selfpath, argv);
perror("Cannot re-execute as 32-bit process");
abort();
}
#endif