Skip to content
Open
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
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ set (I386 "^(i[3-7]|x)86$")
set (AMD64 "^(x86_|x86-|AMD|amd|x)64$")
set (ARM32 "^(arm|ARM|A)(32|v7l|v6l)?$")
set (ARM64 "^(arm|ARM|A|aarch|AARCH)64$")
set (RISCV64 "^riscv64$")

if (MIMICK_TARGET_ARCH)
set(_ARCH "${MIMICK_TARGET_ARCH}")
Expand Down Expand Up @@ -89,6 +90,11 @@ else ()
set (MMK_ABI "aarch64")
set (MMK_BITS 64)
set (MMK_ARCH_ARM64 1)
elseif (_ARCH MATCHES "${RISCV64}")
set (MMK_ARCH "riscv64")
set (MMK_ABI "riscv64")
set (MMK_BITS 64)
set (MMK_ARCH_RISCV64 1)
else ()
message (FATAL_ERROR "Architecture '${_ARCH}' is not supported.")
endif ()
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Supported Compilers: GCC 4.6+, Clang 3.5+, MSVC 14+
| ARM | ✓ | ? | ✓ | ✕ |
| ARM64 | ✓ | ? | ? | ∄ |
| PPC | ✕ | ✕ | ✕ | ∄ |
| RISCV32 | ✕ | ∄ | ✕ | ∄ |
| RISCV64 | ✓ | ∄ | ? | ∄ |

## F.A.Q.

Expand Down
112 changes: 112 additions & 0 deletions src/asm/trampoline-riscv64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Ziyao <ziyao@disroot.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "mangling.h"

.global MANGLE(mmk_trampoline), MANGLE(mmk_trampoline_end)
MANGLE(mmk_trampoline):

/*
* struct mmk_stub* 8bytes
* plt_fn* 8bytes
* trampoline code ...
* =>
* start: jal t0, next 4bytes
*/
start:
jal t0, next // 4 bytes for an instruction
next:
add sp, sp, -8 * 20 // a0 - a7 + t0 + ra +
// *struct mmk_stub aligned to
// 16bytes
sd t0, 0(sp) // 0(sp) label next
ld t0, -20(t0) // t0 -> struct mmk_stub*
sd t0, 8(sp) // 8(sp) struct mmk_stub*
sd ra, 16(sp)

/* Save caller arguments */
sd a0, 24(sp)
sd a1, 32(sp)
sd a2, 40(sp)
sd a3, 48(sp)
sd a4, 56(sp)
sd a5, 64(sp)
sd a6, 72(sp)
sd a7, 80(sp)
fsd fa0, 88(sp)
fsd fa1, 96(sp)
fsd fa2, 104(sp)
fsd fa3, 112(sp)
fsd fa4, 120(sp)
fsd fa5, 128(sp)
fsd fa6, 136(sp)
fsd fa7, 144(sp)
// 8 bytes padding

mv a0, t0
ld ra, 8(t0) // ctx_set
jalr ra

ld t0, 8(sp)

ld ra, 0(t0) // ctx_asked
jalr ra
mv t0, a0

/* Restore caller arguments */
ld a0, 24(sp)
ld a1, 32(sp)
ld a2, 40(sp)
ld a3, 48(sp)
ld a4, 56(sp)
ld a5, 64(sp)
ld a6, 72(sp)
ld a7, 80(sp)
fld fa0, 88(sp)
fld fa1, 96(sp)
fld fa2, 104(sp)
fld fa3, 112(sp)
fld fa4, 120(sp)
fld fa5, 128(sp)
fld fa6, 136(sp)
fld fa7, 144(sp)

bnez t0, return_context
/* stub trampoline jump */
ld t0, 0(sp)
ld t0, -12(t0)
ld ra, 16(sp)
add sp, sp, 8 * 20
jr t0

return_context:
ld t0, 8(sp)
ld ra, 16(t0) // struct mmk_stub* -> ctx_get()
jalr ra
ld ra, 16(sp)
add sp, sp, 8 * 20
ret

MANGLE(mmk_trampoline_end):
nop
67 changes: 62 additions & 5 deletions src/plt-elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,72 @@ static ElfW(Sym) *elf_hash_find(ElfW(Word) *hash, ElfW(Sym) *symtab,
return NULL;
}

struct gnu_hash_header {
uint32_t nbuckets;
uint32_t symoffset;
uint32_t bloom_size;
uint32_t bloom_shift;
};

static uint32_t gnu_hash(const char *s)
{
const uint8_t *name = (const uint8_t *)s;
uint32_t h = 5381;
while (*name)
h = (h << 5) + h + *name++;
return h;
}

static ElfW(Sym) *gnu_hash_find(struct gnu_hash_header *gnuhash, ElfW(Sym) *symtab,
const char *strtab, const char *name)
{
ElfW(Off) *bloom = (ElfW(Off) *)(gnuhash + 1);
uint32_t *buckets = (uint32_t *)(bloom + gnuhash->bloom_size);
uint32_t *chains = buckets + gnuhash->nbuckets;

uint32_t symhash = gnu_hash(name);

// Grab the bloom filter entry, and test both h1 and h2 are present.
ElfW(Off) filter = bloom[(symhash / MMK_BITS) % gnuhash->bloom_size];
if ((filter & (1UL << symhash % MMK_BITS)) == 0)
return NULL;
if ((filter & (1UL << (symhash >> gnuhash->bloom_shift) % MMK_BITS)) == 0)
return NULL;

for (uint32_t idx = buckets[symhash % gnuhash->nbuckets];;++idx) {
uint32_t chainhash = chains[idx - gnuhash->symoffset];
if ((chainhash | 1) == (symhash | 1) &&
strcmp(strtab + symtab[idx].st_name, name) == 0)
return &symtab[idx];
if ((chainhash & 1) != 0)
break;
}
return NULL;
}

static ElfW(Sym) *sym_lookup_dyn(plt_lib lib, const char *name)
{
ElfW(Word) *hash = (ElfW(Word)*) lib_dt_lookup(lib, DT_HASH);
ElfW(Sym) *symtab = (ElfW(Sym)*) lib_dt_lookup(lib, DT_SYMTAB);
const char *strtab = (const char*) lib_dt_lookup(lib, DT_STRTAB);
ElfW(Sym) *symtab = lib_dt_lookup(lib, DT_SYMTAB);
const char *strtab = lib_dt_lookup(lib, DT_STRTAB);

if (!hash || !symtab || !strtab)
if (!symtab || !strtab)
return NULL;
return elf_hash_find (hash, symtab, strtab, name);

#ifdef DT_GNU_HASH
// trust GNU hash if we have it.
struct gnu_hash_header *gnu_hash = lib_dt_lookup(lib, DT_GNU_HASH);
if (gnu_hash)
return gnu_hash_find (gnu_hash, symtab, strtab, name);
#endif

// Look up symbol table using traditional ELF hash.
ElfW(Word) *hash = lib_dt_lookup(lib, DT_HASH);
if (hash)
return elf_hash_find (hash, symtab, strtab, name);

// XXX: we could do a linear walk of the symbol table here...
return NULL;

}

plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name)
Expand Down
27 changes: 27 additions & 0 deletions test/libfoo.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,30 @@ int fn_ii_va(int i, ...)
(void) i;
abort();
}

void fn_vf(float f)
{
(void)f;
abort();
return;
}

float fn_ff(float f)
{
(void)f;
abort();
return f;
}

void fn_vd(double d)
{
(void)d;
abort();
return;
}

double fn_dd(double d)
{
abort();
return d;
}
4 changes: 4 additions & 0 deletions test/libfoo.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ FOO_API void fn_vli(long l, int i);
FOO_API int fn_ili(long l, int i);
FOO_API void fn_vi_va(int i, ...);
FOO_API int fn_ii_va(int i, ...);
FOO_API void fn_vf(float f);
FOO_API float fn_ff(float f);
FOO_API void fn_vd(double d);
FOO_API double fn_dd(double d);

# ifdef __cplusplus
}
Expand Down
29 changes: 29 additions & 0 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ int int_eq(int val) {
return val == expected_int;
}

mmk_mock_define (fn_dd_mock, double, double);

double mul_two(double d)
{
return d * 2;
}

mmk_mock_define (fn_ff_mock, float, float);
float mmk_f_abs(float f)
{
return f < 0 ? -f : f;
}

#define check_called_exact(Expr, Times) do { \
size_t times = (Times); \
if (times == 0) { \
Expand All @@ -66,6 +79,8 @@ int int_eq(int val) {
} \
} while (0)

#define mmk_abs(x) ((x) < 0 ? -(x) : (x))

int main(void)
{
mmk_mock("fn_vv", fn_vv_mock);
Expand Down Expand Up @@ -130,5 +145,19 @@ int main(void)

mmk_reset(fn_ii_va);

mmk_mock("fn_dd", fn_dd_mock);
mmk_when(fn_dd(mmk_any(double)), .then_call = (mmk_fn) mul_two);
mmk_assert(mmk_abs(fn_dd(2.) - 4.) < 1e-4);
mmk_assert(mmk_abs(fn_dd(10.) - 20.) < 1e-4);
mmk_reset(fn_dd);

mmk_mock("fn_ff", fn_ff_mock);
mmk_when(fn_ff(mmk_any(float)), .then_call = (mmk_fn) mmk_f_abs);
mmk_assert(mmk_abs(1.) == fn_ff(1.));
mmk_assert(mmk_abs(-42.) == fn_ff(-42.));
mmk_assert(mmk_abs(-21.) == fn_ff(21.));
mmk_assert(mmk_abs(4.) == fn_ff(-4.));
mmk_reset(fn_ff);

return 0;
}