Skip to content

Add missing compiler-rt files from emscripten #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler-rt/__trap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void __trap() {
__builtin_trap();
}
23 changes: 23 additions & 0 deletions compiler-rt/emscripten_exception_builtins.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2018 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*
* Support functions for emscripten setjmp/longjmp and exception handling
* support. References to the things below are generated in the LLVM backend.
* See: https://llvm.org/doxygen/WebAssemblyLowerEmscriptenEHSjLj_8cpp.html
*/

#include <stdint.h>
#include <threads.h>

thread_local uintptr_t __THREW__ = 0;
thread_local int __threwValue = 0;

void setThrew(uintptr_t threw, int value) {
if (__THREW__ == 0) {
__THREW__ = threw;
__threwValue = value;
}
}
93 changes: 93 additions & 0 deletions compiler-rt/emscripten_setjmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2020 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <stdint.h>
#include <stdlib.h>
#include <setjmp.h>
#include <threads.h>

// 0 - Nothing thrown
// 1 - Exception thrown
// Other values - jmpbuf pointer in the case that longjmp was thrown
static uintptr_t setjmpId = 0;

typedef struct TableEntry {
uintptr_t id;
uint32_t label;
} TableEntry;

extern void setTempRet0(uint32_t value);
extern void setThrew(uintptr_t threw, int value);

TableEntry* saveSetjmp(uintptr_t* env, uint32_t label, TableEntry* table, uint32_t size) {
// Not particularly fast: slow table lookup of setjmpId to label. But setjmp
// prevents relooping anyhow, so slowness is to be expected. And typical case
// is 1 setjmp per invocation, or less.
uint32_t i = 0;
setjmpId++;
*env = setjmpId;
while (i < size) {
if (table[i].id == 0) {
table[i].id = setjmpId;
table[i].label = label;
// prepare next slot
table[i + 1].id = 0;
setTempRet0(size);
return table;
}
i++;
}
// grow the table
size *= 2;
table = (TableEntry*)realloc(table, sizeof(TableEntry) * (size +1));
table = saveSetjmp(env, label, table, size);
setTempRet0(size); // FIXME: unneeded?
return table;
}

uint32_t testSetjmp(uintptr_t id, TableEntry* table, uint32_t size) {
uint32_t i = 0;
while (i < size) {
uintptr_t curr = table[i].id;
if (curr == 0) break;
if (curr == id) {
return table[i].label;
}
i++;
}
return 0;
}

#if !defined(__USING_WASM_SJLJ__)

#include "emscripten_internal.h"

void emscripten_longjmp(uintptr_t env, int val) {
setThrew(env, val);
_emscripten_throw_longjmp();
}
#endif

#ifdef __USING_WASM_SJLJ__

struct __WasmLongjmpArgs {
void *env;
int val;
};

thread_local struct __WasmLongjmpArgs __wasm_longjmp_args;

// Wasm EH allows us to throw and catch multiple values, but that requires
// multivalue support in the toolchain, whch is not reliable at the time.
// TODO Consider switching to throwing two values at the same time later.
void __wasm_longjmp(void *env, int val) {
__wasm_longjmp_args.env = env;
__wasm_longjmp_args.val = val;
__builtin_wasm_throw(1, &__wasm_longjmp_args);
}

#endif
26 changes: 26 additions & 0 deletions compiler-rt/emscripten_tempret.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.globaltype tempRet0, i32
tempRet0:

.globl setTempRet0
setTempRet0:
.functype setTempRet0 (i32) -> ()
local.get 0
global.set tempRet0
end_function

.globl getTempRet0
getTempRet0:
.functype getTempRet0 () -> (i32)
global.get tempRet0
end_function

# These aliases exist solely for LegalizeJSInterface pass in binaryen
# They get exported by emcc and the exports are then removed by the
# binaryen pass
.globl __get_temp_ret
.type __get_temp_ret, @function
__get_temp_ret = getTempRet0

.globl __set_temp_ret
.type __set_temp_ret, @function
__set_temp_ret = setTempRet0
116 changes: 116 additions & 0 deletions compiler-rt/lib/asan/asan_emscripten.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_poisoning.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "lsan/lsan_common.h" // for CAN_SANITIZE_LEAKS

#if SANITIZER_EMSCRIPTEN
#include <emscripten.h>
#include <emscripten/heap.h>
#include <cassert>
#include <cstddef>
#include <pthread.h>
#define __ATTRP_C11_THREAD ((void*)(uptr)-1)

namespace __asan {

void InitializeShadowMemory() {
// Poison the shadow memory of the shadow area at the start of the address
// space. This helps catching null pointer dereference.
FastPoisonShadow(kLowShadowBeg, kLowShadowEnd - kLowShadowBeg, 0xff);

// Assert that the shadow region is large enough. We don't want to start
// running into the static data region which starts right after the shadow
// region.
uptr max_address =
(__builtin_wasm_memory_size(0) * uint64_t(WASM_PAGE_SIZE)) - 1;
uptr max_shadow_address = MEM_TO_SHADOW(max_address);
// TODO(sbc): In the growable memory case we should really be checking this
// every time we grow.
assert(max_shadow_address <= kLowShadowEnd && "shadow region is too small");
}

void AsanCheckDynamicRTPrereqs() {}
void AsanCheckIncompatibleRT() {}
void InitializePlatformInterceptors() {}
void InitializePlatformExceptionHandlers() {}
bool IsSystemHeapAddress (uptr addr) { return false; }

void *AsanDoesNotSupportStaticLinkage() {
// On Linux, this is some magic that fails linking with -static.
// On Emscripten, we have to do static linking, so we stub this out.
return nullptr;
}

void InitializeAsanInterceptors() {}

void FlushUnneededASanShadowMemory(uptr p, uptr size) {}

extern "C" {
int emscripten_builtin_pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*callback)(void *), void *arg);
}

static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread *)arg;
SetCurrentThread(t);
return t->ThreadStart(GetTid());
}

INTERCEPTOR(int, pthread_create, pthread_t *thread,
const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) {
EnsureMainThreadIDIsCorrect();
// Strict init-order checking is thread-hostile.
if (flags()->strict_init_order)
StopInitOrderChecking();
GET_STACK_TRACE_THREAD;
int detached = 0;
if (attr && attr != __ATTRP_C11_THREAD)
pthread_attr_getdetachstate(attr, &detached);

u32 current_tid = GetCurrentTidOrInvalid();
AsanThread *t =
AsanThread::Create(start_routine, arg, current_tid, &stack, detached);

int result;
{
// Ignore all allocations made by pthread_create: thread stack/TLS may be
// stored by pthread for future reuse even after thread destruction, and
// the linked list it's stored in doesn't even hold valid pointers to the
// objects, the latter are calculated by obscure pointer arithmetic.
#if CAN_SANITIZE_LEAKS
__lsan::ScopedInterceptorDisabler disabler;
#endif
result = REAL(pthread_create)(thread, attr, asan_thread_start, t);
}
if (result != 0) {
// If the thread didn't start delete the AsanThread to avoid leaking it.
// Note AsanThreadContexts never get destroyed so the AsanThreadContext
// that was just created for the AsanThread is wasted.
t->Destroy();
}
return result;
}

} // namespace __asan

namespace __lsan {

#ifndef __EMSCRIPTEN_PTHREADS__
// XXX HACK: Emscripten treats thread_local variables the same as globals in
// non-threaded builds, so a hack was introduced where we skip the allocator
// cache in the common module. Now we have to define this symbol to keep that
// hack working when using LSan as part of ASan without threads.
void GetAllocatorCacheRange(uptr *begin, uptr *end) {
*begin = *end = 0;
}
#endif

u32 GetCurrentThread() { return __asan::GetCurrentThread()->tid(); }

} // namespace __lsan

#endif // SANITIZER_EMSCRIPTEN
88 changes: 88 additions & 0 deletions compiler-rt/lib/asan/asan_mapping_emscripten.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===-- asan_mapping_emscripten.h -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Emscripten-specific definitions for ASan memory mapping.
//===----------------------------------------------------------------------===//
#ifndef ASAN_MAPPING_EMSCRIPTEN_H
#define ASAN_MAPPING_EMSCRIPTEN_H

extern char __global_base;

#define kLowMemBeg ((uptr) &__global_base)
#define kLowMemEnd ((kLowShadowBeg << ASAN_SHADOW_SCALE) - 1)

#define kLowShadowBeg 0
#define kLowShadowEnd ((uptr) &__global_base - 1)

#define kHighMemBeg 0

#define kHighShadowBeg 0
#define kHighShadowEnd 0

#define kMidShadowBeg 0
#define kMidShadowEnd 0

#define kShadowGapBeg (kLowMemEnd + 1)
#define kShadowGapEnd 0xFFFFFFFF

#define kShadowGap2Beg 0
#define kShadowGap2End 0

#define kShadowGap3Beg 0
#define kShadowGap3End 0

// The first 1/8 of the shadow memory space is shadowing itself.
// This allows attempted accesses into the shadow memory, as well as null
// pointer dereferences, to be detected properly.
// The shadow memory of the shadow memory is poisoned.
#define MEM_TO_SHADOW(mem) ((mem) >> ASAN_SHADOW_SCALE)
#define SHADOW_TO_MEM(mem) ((mem) << ASAN_SHADOW_SCALE)

namespace __asan {

static inline bool AddrIsInLowMem(uptr a) {
PROFILE_ASAN_MAPPING();
return a >= kLowMemBeg && a <= kLowMemEnd;
}

static inline bool AddrIsInLowShadow(uptr a) {
PROFILE_ASAN_MAPPING();
return a >= kLowShadowBeg && a <= kLowShadowEnd;
}

static inline bool AddrIsInMidMem(uptr a) {
PROFILE_ASAN_MAPPING();
return false;
}

static inline bool AddrIsInMidShadow(uptr a) {
PROFILE_ASAN_MAPPING();
return false;
}

static inline bool AddrIsInHighMem(uptr a) {
PROFILE_ASAN_MAPPING();
return false;
}

static inline bool AddrIsInHighShadow(uptr a) {
PROFILE_ASAN_MAPPING();
return false;
}

static inline bool AddrIsInShadowGap(uptr a) {
PROFILE_ASAN_MAPPING();
return a >= kShadowGapBeg;
}

} // namespace __asan

#endif // ASAN_MAPPING_EMSCRIPTEN_H
Loading