Rocksolid Light

Welcome to RetroBBS

mail  files  register  newsreader  groups  login

Message-ID:  

Well, Jim, I'm not much of an actor either.


devel / comp.lang.c++ / Re: Custom stack for function invocation (any signature)

Re: Custom stack for function invocation (any signature)

<c74af3de-4998-4960-8c70-f56abc594b85n@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=879&group=comp.lang.c%2B%2B#879

  copy link   Newsgroups: comp.lang.c++
X-Received: by 2002:a05:620a:f12:b0:765:a78f:dd24 with SMTP id v18-20020a05620a0f1200b00765a78fdd24mr50356qkl.9.1689428536423;
Sat, 15 Jul 2023 06:42:16 -0700 (PDT)
X-Received: by 2002:a4a:458c:0:b0:566:238a:3c55 with SMTP id
y134-20020a4a458c000000b00566238a3c55mr1485852ooa.1.1689428536106; Sat, 15
Jul 2023 06:42:16 -0700 (PDT)
Path: i2pn2.org!i2pn.org!usenet.goja.nl.eu.org!3.eu.feeder.erje.net!feeder.erje.net!fdn.fr!proxad.net!feeder1-2.proxad.net!209.85.160.216.MISMATCH!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.c++
Date: Sat, 15 Jul 2023 06:42:15 -0700 (PDT)
In-Reply-To: <d19d9ffa-cb8d-425f-99f4-b5a914e67cc8n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=94.119.64.57; posting-account=w4UqJAoAAAAYC-PItfDbDoVGcg0yISyA
NNTP-Posting-Host: 94.119.64.57
References: <05d2fc9e-088e-41a6-9039-2de1717ba952n@googlegroups.com>
<4b5f0bd6-0816-4800-b8bd-adfae203932cn@googlegroups.com> <990646ef-4d2c-411b-8c19-e90cd3eacbfcn@googlegroups.com>
<d685c246-5c1b-4640-86c6-b20dd271287dn@googlegroups.com> <84f722fb-1263-4226-aa3b-d19c7c81b00cn@googlegroups.com>
<770c748e-6b98-4c08-8d9f-493e1ef0edc4n@googlegroups.com> <d19d9ffa-cb8d-425f-99f4-b5a914e67cc8n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <c74af3de-4998-4960-8c70-f56abc594b85n@googlegroups.com>
Subject: Re: Custom stack for function invocation (any signature)
From: cauldwell.thomas@gmail.com (Frederick Virchanza Gotham)
Injection-Date: Sat, 15 Jul 2023 13:42:16 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
 by: Frederick Virchanza - Sat, 15 Jul 2023 13:42 UTC

On Saturday, July 15, 2023 at 2:56:08 AM UTC+1, Frederick Virchanza Gotham wrote:
>
> I'm trying to support exception handling now but the following is segfaulting:

I'm trying to get it working with exception handling, but it's segfaulting. I've tried copying the entire stack but it's still segfaulting inside libgcc inside the function "uw_update_context_1" when it tries to copy the context struct. Anybody know what's wrong with the following?

https://godbolt.org/z/vv7hTGWdr

And here it is copy-pasted:

#include <cassert> // assert
#include <cstddef> // size_t
#include <climits> // ULONG_LONG_MAX
#include <cstdlib> // strtoull
#include <cstring> // strstr
#include <cstdint> // UINTPTR_MAX
#include <memory> // unique_ptr
#include <utility> // forward
#include <exception> // exception_ptr, current_exception
#include <type_traits> // is_rvalue_reference, is_trivially_destructible

#include <iostream> // cout, endl ============================= REMOVE THIS
using std::cerr, std::cout, std::endl;

#include <unistd.h> // lseek, read, close
#include <fcntl.h> // open

char *GetStackBottom(void) noexcept
{ static thread_local int fd = -1;

if ( fd < 0 )
{
fd = ::open("/proc/thread-self/maps", O_RDONLY);

if ( fd < 0 ) return nullptr;

// std::atexit( [](){ ::close(fd); } ); - Won't work
}

if ( 0 != ::lseek(fd, 0, SEEK_SET) ) return nullptr;

static char buf[8192u];
if ( ::read(fd, buf, 8191u) < 32 ) return nullptr;
buf[8191u] = '\0';

char *vm = std::strstr(buf, "[stack]\n");
if ( nullptr == vm ) return nullptr;

while ( '\n' != *vm ) --vm;

++vm;

while ( '-' != *vm ) ++vm;

char *vm2 = ++vm;

while ( ' ' != *vm2 ) ++vm2;

*vm2 = '\0';

static_assert( ULONG_LONG_MAX >= UINTPTR_MAX );
long long unsigned const addr = std::strtoull(vm,nullptr,16u);

return reinterpret_cast<char*>(addr);
}

thread_local char *p_original, *p_replacement;
thread_local void (*f)(void), (*g)(void);
thread_local char *bottom_of_stack;
thread_local std::exception_ptr e;

extern "C" {
void Assembler_set_bottom_of_stack (void) noexcept;
void Assembler_set_stack_pointer_and_invoke(void) noexcept;
}

__asm("Assembler_set_bottom_of_stack: \n"
".intel_syntax noprefix \n"
" mov r10, rsp \n"
" add r10, 16 \n" // +8 return addr, +8 to be safe
" mov QWORD PTR fs:bottom_of_stack@tpoff, r10 \n"
" ret \n"
".att_syntax");

template<typename T> requires std::is_trivially_destructible_v<std::remove_cvref_t<T> >
std::remove_cvref_t<T> dummy_prvalue(void) noexcept
{ typedef std::remove_cvref_t<T> TT;
void (*const tmp)(void) = [](){};
TT (*const funcptr)(void) = reinterpret_cast<TT(*)(void)>(tmp);
return funcptr(); // guaranteed elision of move/copy operations here
}

template<typename R, typename... Params>
class Invoker {

static R exception_capable(Params... args) noexcept
{
cerr << "Entered exception_capable\n";

R (*const funcptr)(Params...) = reinterpret_cast<R(*)(Params...)>(g);

try
{
cerr << "Entered try-block\n";
return funcptr( std::forward<Params>(args)... );
}
catch (...)
{
cerr << "Exception thrown!\n";
e = std::current_exception();
}

return dummy_prvalue<R>();
}

Invoker(char *const arg_p, R(*const arg_f)(Params...)) noexcept
{
p_replacement = arg_p; // sets a thread_local variable
g = reinterpret_cast<void (*)(void)>(arg_f); // sets a thread_local variable
f = reinterpret_cast<void (*)(void)>(exception_capable); // sets a thread_local variable
}

public:
R operator()(Params... args) noexcept(false) // This could be static function but I like operator()
{
//Assembler_set_bottom_of_stack();
//cout << "\nBottom of stack: " << (void*)bottom_of_stack << " (my own assembler)\n";
bottom_of_stack = GetStackBottom() - 8u;
//cout << "Bottom of stack: " << (void*)bottom_of_stack << " (thread-self/maps)\n";
e = nullptr;
R (*const funcptr)(Params...) = reinterpret_cast<R(*)(Params...)>(Assembler_set_stack_pointer_and_invoke);
R retval = funcptr( std::forward<Params>(args)... );
if ( nullptr != e )
{
cerr << "About to rethrow!\n";
std::rethrow_exception(e);
}
return retval;
}

friend class Stacker;
};

class Stacker {
char *p;
std::unique_ptr<char[]> mystack;

public:

Stacker(std::size_t const len) noexcept(false) // might throw bad_alloc
{
assert( len >= 128u );

mystack.reset( new char[len] );
p = mystack.get() + len - 16u;
}

Stacker(char *const arg, std::size_t const len) noexcept
{
assert( nullptr != arg );
assert( len >= 128u );

p = arg + len - 16u;
}

template<typename R, typename... Params>
Invoker<R,Params...> operator()( R(*const arg)(Params...) ) noexcept
{
return Invoker<R,Params...>(this->p, arg);
}
};

/* In the following function written in x86_64 assembler using
the System V calling convention, we can only clobber r10
and r11 because all of the other caller-saved registers
must be preserved for the 'jmp' to the target function. */

__asm("Assembler_set_stack_pointer_and_invoke:\n"
".intel_syntax noprefix \n"
// Step 1: Save the original stack pointer
" mov QWORD PTR fs:p_original@tpoff, rsp \n"
// Step 2: Retrieve the replacement stack pointer
" push r15 \n" // save to restore later
" mov r15, rsp \n" // pointer to the r15 we just pushed onto stack
" add r15, 8 \n" // sets 'r15' to top of old stack
" mov r10, QWORD PTR fs:p_replacement@tpoff \n" // sets 'r10' to top of new stack
" mov rax, QWORD PTR fs:bottom_of_stack@tpoff \n" // sets 'rax' to bottom of old stack
// Right now: R15 is the top of the old stack
// R10 is the top of the new stack
// RAX is the bottom of the old stack
// We want to do:
// while ( rax != r15 ) *r10-- = *rax--;
// Step 3: Copy the old stack to the new stack (it might contain supernumerary arguments or a big return struct)
" jmp cond \n" // Jump to condition of 'while' loop
"loop: \n" // ----<----<----<----<----
" mov r11, qword ptr [rax] \n" // |
" mov qword ptr [r10], r11 \n" // ^
" sub r10, 1 \n" // | Loop
" sub rax, 1 \n" // |
"cond: \n" // ^
" cmp rax, r15 \n" // |
" jne loop \n" // ---->---->---->---->----
" pop r15 \n" // restore original value
// Step 4: Change the stack pointer to the new stack ============================================ " mov rsp, r10 \n" // ================================================= new stack
// Step 5: Set the return address to after the 'jmp' instruction
" lea r10, [Label_Jump_Back] \n"
" add rsp, 8 \n" // This line and the next line replace the return address on the stack
" push r10 \n" // This line and the previous line replace the return address on the stack
// Step 6: Invoke the function
" jmp QWORD PTR fs:f@tpoff \n" // --- Invoke the function!
"Label_Jump_Back: \n"
// Note: The label has already been popped off the stack by the callee
// Step 7: Restore the original stack pointer
" mov rsp, QWORD PTR fs:p_original@tpoff \n"
// Step 8: Jump back to the original address
" ret \n"
".att_syntax");

// =================== And now the test code ==============================================
#include <iostream> // cout, endl
using std::cout, std::endl;

struct VeryBigStruct {
double a[3];
int b[3];
double c[3];
int d[3];
double e[3];
int f[3];
};

VeryBigStruct Func2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10)
{ cerr << "Entered Func2\n";
VeryBigStruct vbs;
vbs.f[2] = a1+a2+a3+a4+a5+a6+a7+a8+a9+a10;
throw int(3);
return vbs;
}

int main(void)
{ cout << "first line in main\n";

#if 1
try
{
cout << "Retval: " << Func2(1,2,7,4,5,6,7,8,9,10).f[2] << endl;
}
catch (int const n)
{
cout << "Caught an int: " << n << endl;
}
#endif

try
{
cout << "Retval: " << Stacker(1048576000u)(Func2)(1,2,7,4,5,6,7,8,9,10).f[2] << endl;
}
catch (int const n)
{
cout << "Caught an int: " << n << endl;
}

cout << "last line in main\n";
}

SubjectRepliesAuthor
o Custom stack for function invocation (any signature)

By: Frederick Virchanza on Thu, 13 Jul 2023

7Frederick Virchanza Gotham
server_pubkey.txt

rocksolid light 0.9.81
clearnet tor