/*
Solusi pemuatan ulang langsung khusus header file tunggal untuk C, ditulis dalam C++:
CATATAN: Satu-satunya file yang penting dalam repositori ini adalah cr.h
.
File ini berisi dokumentasi penurunan harga, lisensi, implementasi, dan api publik. Semua file lain dalam repositori ini adalah file pendukung dan dapat diabaikan dengan aman.
Anda dapat mengunduh dan menginstal cr menggunakan manajer ketergantungan vcpkg:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install cr
Port cr di vcpkg selalu diperbarui oleh anggota tim Microsoft dan kontributor komunitas. Jika versinya sudah kedaluwarsa, silakan buat masalah atau tarik permintaan pada repositori vcpkg.
Aplikasi host (tipis) yang dapat dieksekusi akan menggunakan cr
untuk mengelola pemuatan ulang langsung aplikasi sebenarnya dalam bentuk biner dinamis yang dapat dimuat, host akan berbentuk seperti:
#define CR_HOST // required in the host only and before including cr.h
#include "../cr.h"
int main ( int argc , char * argv []) {
// the host application should initalize a plugin with a context, a plugin
cr_plugin ctx ;
// the full path to the live-reloadable application
cr_plugin_open ( ctx , "c:/path/to/build/game.dll" );
// call the update function at any frequency matters to you, this will give
// the real application a chance to run
while (! cr_plugin_update ( ctx )) {
// do anything you need to do on host side (ie. windowing and input stuff?)
}
// at the end do not forget to cleanup the plugin context
cr_plugin_close ( ctx );
return 0 ;
}
Sedangkan tamu (aplikasi nyata), akan seperti:
CR_EXPORT int cr_main ( struct cr_plugin * ctx , enum cr_op operation ) {
assert ( ctx );
switch ( operation ) {
case CR_LOAD : return on_load (...); // loading back from a reload
case CR_UNLOAD : return on_unload (...); // preparing to a new reload
case CR_CLOSE : ...; // the plugin will close and not reload anymore
}
// CR_STEP
return on_update (...);
}
CR_INITIAL_FAILURE
. Jika plugin awal rusak, host harus menentukan jalur selanjutnya, dan kami tidak akan memuat ulang plugin yang rusak. cr_plugin_load
tidak digunakan lagi dan digantikan dengan cr_plugin_open
untuk konsistensi dengan cr_plugin_close
. Lihat edisi #49.CR_BAD_IMAGE
jika file biner masih belum siap meskipun stempel waktunya berubah. Ini bisa terjadi jika pembuatan file (kompiler atau penyalinan) lambat.CR_BAD_IMAGE
). Sekarang versinya dikurangi satu kali di pengendali kerusakan dan kemudian di lain waktu selama rollback dan kemudian diubah lagi. Rollback karena gambar yang tidak lengkap tidak akan salah melakukan rollback dua versi, ini akan berlanjut pada versi yang sama, mencoba memuat ulang hingga gambar tersebut valid (salinan atau kompiler selesai menulis ke dalamnya). Hal ini mungkin berdampak pada penggunaan cr
saat ini jika info version
digunakan selama CR_UNLOAD
karena sekarang nilainya akan berbeda. Dua sampel sederhana dapat ditemukan di direktori samples
.
Yang pertama adalah aplikasi konsol sederhana yang mendemonstrasikan beberapa keadaan statis dasar yang bekerja antara instance dan pengujian penanganan kerusakan dasar. Cetak ke keluaran digunakan untuk menunjukkan apa yang terjadi.
Yang kedua menunjukkan cara live-reload aplikasi opengl menggunakan Dear ImGui. Beberapa negara bagian berada di sisi tuan rumah sementara sebagian besar kode berada di sisi tamu.
Sampel dan pengujian menggunakan sistem build fips. Itu membutuhkan Python dan CMake.
$ ./fips build # will generate and build all artifacts
$ ./fips run crTest # To run tests
$ ./fips run imgui_host # To run imgui sample
# open a new console, then modify imgui_guest.cpp
$ ./fips make imgui_guest # to build and force imgui sample live reload
int (*cr_main)(struct cr_plugin *ctx, enum cr_op operation)
Ini adalah penunjuk fungsi ke fungsi titik masuk biner dinamis yang dapat dimuat.
Argumen
ctx
ke konteks yang akan diteruskan dari host
ke guest
yang berisi informasi berharga tentang versi yang dimuat saat ini, alasan kegagalan, dan data pengguna. Untuk info lebih lanjut lihat cr_plugin
.operation
operasi mana yang sedang dijalankan, lihat cr_op
.Kembali
CR_USER
. 0 atau nilai positif yang akan diteruskan ke proses host
. bool cr_plugin_open(cr_plugin &ctx, const char *fullpath)
Memuat dan menginisialisasi plugin.
Argumen
ctx
konteks yang akan mengelola data internal plugin dan data pengguna.fullpath
path lengkap dengan nama file ke biner yang dapat dimuat untuk plugin atau NULL
.Kembali
true
jika berhasil, false
sebaliknya. void cr_set_temporary_path(cr_plugin& ctx, const std::string &path)
Menetapkan jalur sementara ke mana salinan sementara plugin akan ditempatkan. Harus dipanggil segera setelah cr_plugin_open()
. Jika jalur temporary
tidak disetel, salinan sementara file akan disalin ke direktori yang sama di mana file asli berada.
Argumen
ctx
konteks yang akan mengelola data internal plugin dan data pengguna.path
path lengkap ke direktori yang ada yang akan digunakan untuk menyimpan salinan plugin sementara. int cr_plugin_update(cr_plugin &ctx, bool reloadCheck = true)
Fungsi ini akan memanggil fungsi plugin cr_main
. Ini harus dipanggil sesering kebutuhan logika/aplikasi inti.
Argumen
ctx
data konteks plugin saat ini.reloadCheck
opsional: lakukan pemeriksaan disk (stat()) untuk melihat apakah perpustakaan dinamis perlu dimuat ulang.Kembali
cr_main
. void cr_plugin_close(cr_plugin &ctx)
Bersihkan status internal setelah plugin tidak diperlukan lagi.
Argumen
ctx
data konteks plugin saat ini. cr_op
Enum menunjukkan jenis langkah yang dijalankan oleh host
:
CR_LOAD
Beban yang disebabkan oleh pemuatan ulang sedang dijalankan, dapat digunakan untuk memulihkan keadaan internal yang disimpan.CR_STEP
Pembaruan aplikasi, ini adalah operasi normal dan paling sering;CR_UNLOAD
Pembongkaran untuk memuat ulang plugin akan dijalankan, memberikan aplikasi satu kesempatan untuk menyimpan data yang diperlukan;CR_CLOSE
Digunakan saat menutup plugin, Ini berfungsi seperti CR_UNLOAD
tetapi tidak ada CR_LOAD
yang diharapkan setelahnya; cr_plugin
Struktur konteks contoh plugin.
p
penunjuk buram untuk data cr internal;userdata
dapat digunakan oleh pengguna untuk meneruskan informasi di antara pemuatan ulang;version
bertambah banyak untuk setiap pemuatan ulang yang berhasil, dimulai dari 1 untuk pemuatan pertama. Versi akan berubah selama proses penanganan kerusakan ;failure
yang digunakan oleh sistem perlindungan kerusakan, akan menyimpan kode kesalahan kegagalan terakhir yang menyebabkan rollback. Lihat cr_failure
untuk info lebih lanjut tentang kemungkinan nilai; cr_failure
Jika terjadi kerusakan pada biner yang dapat dimuat, pengendali kerusakan akan menunjukkan alasan kerusakan tersebut dengan salah satu dari berikut ini:
CR_NONE
Tidak ada kesalahan;CR_SEGFAULT
. SIGSEGV
di Linux/OSX atau EXCEPTION_ACCESS_VIOLATION
di Windows;CR_ILLEGAL
Jika ada instruksi ilegal. SIGILL
di Linux/OSX atau EXCEPTION_ILLEGAL_INSTRUCTION
di Windows;CR_ABORT
Batalkan, SIGBRT
di Linux/OSX, tidak digunakan di Windows;CR_MISALIGN
Bus error, SIGBUS
di Linux/OSX atau EXCEPTION_DATATYPE_MISALIGNMENT
di Windows;CR_BOUNDS
Adalah EXCEPTION_ARRAY_BOUNDS_EXCEEDED
, khusus Windows;CR_STACKOVERFLOW
Adalah EXCEPTION_STACK_OVERFLOW
, khusus Windows;CR_STATE_INVALIDATED
Kegagalan keamanan manajemen CR_STATE
statis;CR_BAD_IMAGE
Plugin ini bukan gambar yang valid (yaitu kompiler mungkin masih menulisnya);CR_OTHER
Sinyal lainnya, khusus Linux;CR_USER
Kesalahan pengguna (untuk nilai negatif yang dikembalikan dari cr_main
); CR_HOST
tentukan Definisi ini harus digunakan sebelum memasukkan cr.h
ke dalam host
, jika CR_HOST
tidak ditentukan, cr.h
akan berfungsi sebagai file header API publik untuk digunakan dalam implementasi guest
.
Secara opsional CR_HOST
juga dapat ditentukan ke salah satu nilai berikut sebagai cara untuk mengonfigurasi mode operasi safety
untuk manajemen keadaan statis otomatis ( CR_STATE
):
CR_SAFEST
Akan memvalidasi alamat dan ukuran bagian data negara selama memuat ulang, jika ada perubahan, beban akan dikembalikan;CR_SAFE
Hanya akan memvalidasi ukuran bagian status, ini berarti bahwa alamat statika dapat berubah (dan yang terbaik adalah menghindari menahan penunjuk apa pun ke hal-hal statis);CR_UNSAFE
Tidak akan memvalidasi apa pun kecuali ukuran bagian yang sesuai, mungkin tidak selalu tepat (bertambah dapat diterima tetapi menyusut tidak), ini adalah perilaku default;CR_DISABLE
Menonaktifkan sepenuhnya manajemen status statis otomatis; CR_STATE
Digunakan untuk menandai variabel statis global atau lokal untuk disimpan dan dipulihkan selama memuat ulang.
Penggunaan
static bool CR_STATE bInitialized = false;
Anda dapat menentukan makro ini sebelum menyertakan cr.h di host (CR_HOST) untuk menyesuaikan alokasi memori cr.h dan perilaku lainnya:
CR_MAIN_FUNC
: mengubah simbol 'cr_main' menjadi nama fungsi yang ditentukan pengguna. bawaan: #define CR_MAIN_FUNC "cr_main"CR_ASSERT
: timpa pernyataan. bawaan: #define CA_ASSERT(e) menegaskan(e)CR_REALLOC
: ganti realloc libc. bawaan: #define CR_REALLOC(ptr, ukuran) ::realloc(ptr, ukuran)CR_MALLOC
: ganti malloc libc. bawaan: #define CR_MALLOC(ukuran) ::malloc(ukuran)CR_FREE
: ganti libc gratis. bawaan: #define CR_FREE(ptr) ::gratis(ptr)CR_DEBUG
: menampilkan pesan debug dalam CR_ERROR, CR_LOG dan CR_TRACECR_ERROR
: mencatat pesan debug ke stderr. default (khusus CR_DEBUG): #define CR_ERROR(...) fprintf(stderr, VA_ARGS )CR_LOG
: mencatat pesan debug. default (khusus CR_DEBUG): #define CR_LOG(...) fprintf(stdout, VA_ARGS )CR_TRACE
: mencetak panggilan fungsi. default (khusus CR_DEBUG): #define CR_TRACE(...) fprintf(stdout, "CR_TRACE: %sn", FUNCTION )A: Baca alasan saya membuat ini di sini.
J: Pastikan host aplikasi dan dll Anda menggunakan run-time dinamis (/MD atau /MDd) karena data apa pun yang dialokasikan di heap harus dibebaskan dengan instance pengalokasi yang sama, dengan membagi run-time antara tamu dan host Anda akan menjamin pengalokasi yang sama digunakan.
J: Ya. Ini seharusnya berfungsi tanpa masalah di Windows. Di Linux dan OSX mungkin ada masalah dengan penanganan kerusakan
Jika Anda harus memuat dll sebelum cr
karena alasan apa pun, Visual Studio mungkin masih mengunci PDB. Anda mungkin mengalami masalah ini dan solusinya ada di sini.
Pertama, pastikan sistem build Anda tidak mengganggu dengan tetap menautkan ke perpustakaan bersama Anda. Ada banyak hal yang bisa salah dan Anda perlu memastikan hanya cr
yang akan menangani perpustakaan bersama Anda. Di linux, untuk info lebih lanjut tentang cara menemukan apa yang terjadi, periksa masalah ini.
cr
adalah C
reloader dan berurusan dengan C diasumsikan hal-hal sederhana sebagian besar akan berhasil.
Masalahnya adalah bagaimana linker akan memutuskan untuk mengatur ulang berbagai hal sesuai dengan jumlah perubahan yang Anda lakukan pada kode. Untuk perubahan bertahap dan terlokalisasi saya tidak pernah mengalami masalah apa pun, secara umum saya hampir tidak mengalami masalah sama sekali dengan menulis kode C normal. Sekarang, ketika segala sesuatunya mulai menjadi lebih kompleks dan mendekati C++, hal ini menjadi lebih berisiko. Jika Anda perlu melakukan hal-hal rumit, saya sarankan memeriksa RCCPP dan membaca PDF ini dan postingan blog asli saya tentang cr
di sini.
Dengan semua informasi ini Anda akan dapat memutuskan mana yang lebih baik untuk kasus penggunaan Anda.
cr
Sponsor Untuk mensponsori port cr
ke MacOSX.
Danny Grein
Rokas Kupstys
Nuh Rinehart
Niklas Lundberg
Sepehr Taghdisian
Robert Gabriel Jakabosky
@pixelherodev
Alexander
Kami menyambut SEMUA kontribusi, tidak ada hal kecil yang dapat disumbangkan, bahkan perbaikan kesalahan ketik satu huruf pun dipersilakan.
Satu-satunya hal yang kami perlukan adalah menguji secara menyeluruh, mempertahankan gaya kode, dan selalu memperbarui dokumentasi.
Juga, menerima dan menyetujui untuk melepaskan kontribusi apa pun di bawah lisensi yang sama.
Lisensi MIT (MIT)
Hak Cipta (c) 2017 Danny Angelo Carminati Grein
Izin dengan ini diberikan, secara gratis, kepada siapa pun yang memperoleh salinan perangkat lunak ini dan file dokumentasi terkait ("Perangkat Lunak"), untuk menggunakan Perangkat Lunak tanpa batasan, termasuk namun tidak terbatas pada hak untuk menggunakan, menyalin, memodifikasi, menggabungkan , mempublikasikan, mendistribusikan, mensublisensikan, dan/atau menjual salinan Perangkat Lunak, dan mengizinkan orang yang menerima Perangkat Lunak untuk melakukan hal tersebut, dengan tunduk pada ketentuan berikut:
Pemberitahuan hak cipta di atas dan pemberitahuan izin ini akan disertakan dalam semua salinan atau sebagian besar Perangkat Lunak.
PERANGKAT LUNAK INI DISEDIAKAN "APA ADANYA", TANPA JAMINAN APA PUN, TERSURAT MAUPUN TERSIRAT, TERMASUK NAMUN TIDAK TERBATAS PADA JAMINAN KELAYAKAN UNTUK DIPERDAGANGKAN, KESESUAIAN UNTUK TUJUAN TERTENTU, DAN TIDAK ADA PELANGGARAN. DALAM KEADAAN APA PUN PENULIS ATAU PEMEGANG HAK CIPTA TIDAK BERTANGGUNG JAWAB ATAS KLAIM, KERUSAKAN, ATAU TANGGUNG JAWAB LAINNYA, BAIK DALAM TINDAKAN KONTRAK, HUKUM ATAU LAINNYA, YANG TIMBUL DARI, ATAU SEHUBUNGAN DENGAN PERANGKAT LUNAK ATAU PENGGUNAAN ATAU HAL-HAL LAIN DALAM PERANGKAT LUNAK.
* /
#ifndef __CR_H__
#define __CR_H__
//
// Global OS specific defines/customizations
//
#if defined( _WIN32 )
#define CR_WINDOWS
#define CR_PLUGIN ( name ) "" name ".dll"
#elif defined( __linux__ )
#define CR_LINUX
#define CR_PLUGIN ( name ) "lib" name ".so"
#elif defined( __APPLE__ )
#define CR_OSX
#define CR_PLUGIN ( name ) "lib" name ".dylib"
#else
#error "Unknown/unsupported platform, please open an issue if you think this
platform should be supported."
#endif // CR_WINDOWS || CR_LINUX || CR_OSX
//
// Global compiler specific defines/customizations
//
#if defined( _MSC_VER )
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#define CR_IMPORT extern "C" __declspec(dllimport)
#else
#define CR_EXPORT __declspec(dllexport)
#define CR_IMPORT __declspec(dllimport)
#endif
#endif // defined(_MSC_VER)
#if defined( __GNUC__ ) // clang & gcc
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define CR_EXPORT __attribute__((visibility("default")))
#endif
#define CR_IMPORT
#endif // defined(__GNUC__)
#if defined( __MINGW32__ )
#undef CR_EXPORT
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#else
#define CR_EXPORT __declspec(dllexport)
#endif
#endif
// cr_mode defines how much we validate global state transfer between
// instances. The default is CR_UNSAFE, you can choose another mode by
// defining CR_HOST, ie.: #define CR_HOST CR_SAFEST
enum cr_mode {
CR_SAFEST = 0 , // validate address and size of the state section, if
// anything changes the load will rollback
CR_SAFE = 1 , // validate only the size of the state section, this means
// that address is assumed to be safe if avoided keeping
// references to global/static states
CR_UNSAFE = 2 , // don't validate anything but that the size of the section
// fits, may not be identical though
CR_DISABLE = 3 // completely disable the auto state transfer
};
// cr_op is passed into the guest process to indicate the current operation
// happening so the process can manage its internal data if it needs.
enum cr_op {
CR_LOAD = 0 ,
CR_STEP = 1 ,
CR_UNLOAD = 2 ,
CR_CLOSE = 3 ,
};
enum cr_failure {
CR_NONE , // No error
CR_SEGFAULT , // SIGSEGV / EXCEPTION_ACCESS_VIOLATION
CR_ILLEGAL , // illegal instruction (SIGILL) / EXCEPTION_ILLEGAL_INSTRUCTION
CR_ABORT , // abort (SIGBRT)
CR_MISALIGN , // bus error (SIGBUS) / EXCEPTION_DATATYPE_MISALIGNMENT
CR_BOUNDS , // EXCEPTION_ARRAY_BOUNDS_EXCEEDED
CR_STACKOVERFLOW , // EXCEPTION_STACK_OVERFLOW
CR_STATE_INVALIDATED , // one or more global data section changed and does
// not safely match basically a failure of
// cr_plugin_validate_sections
CR_BAD_IMAGE , // The binary is not valid - compiler is still writing it
CR_INITIAL_FAILURE , // Plugin version 1 crashed, cannot rollback
CR_OTHER , // Unknown or other signal,
CR_USER = 0x100 ,
};
struct cr_plugin ;
typedef int ( * cr_plugin_main_func )( struct cr_plugin * ctx , enum cr_op operation );
// public interface for the plugin context, this has some user facing
// variables that may be used to manage reload feedback.
// - userdata may be used by the user to pass information between reloads
// - version is the reload counter (after loading the first instance it will
// be 1, not 0)
// - failure is the (platform specific) last error code for any crash that may
// happen to cause a rollback reload used by the crash protection system
struct cr_plugin {
void * p ;
void * userdata ;
unsigned int version ;
enum cr_failure failure ;
unsigned int next_version ;
unsigned int last_working_version ;
};
#ifndef CR_HOST
// Guest specific compiler defines/customizations
#if defined( _MSC_VER )
#pragma section(".state", read, write)
#define CR_STATE __declspec(allocate(".state"))
#endif // defined(_MSC_VER)
#if defined( CR_OSX )
#define CR_STATE __attribute__((used, section("__DATA,__state")))
#else
#if defined( __GNUC__ ) // clang & gcc
#define CR_STATE __attribute__((section(".state")))
#endif // defined(__GNUC__)
#endif
#else // #ifndef CR_HOST
// Overridable macros
#ifndef CR_LOG
# ifdef CR_DEBUG
# include
# define CR_LOG (...) fprintf(stdout, __VA_ARGS__)
# else
# define CR_LOG (...)
# endif
#endif
#ifndef CR_ERROR
# ifdef CR_DEBUG
# include
# define CR_ERROR (...) fprintf(stderr, __VA_ARGS__)
# else
# define CR_ERROR (...)
# endif
#endif
#ifndef CR_TRACE
# ifdef CR_DEBUG
# include
# define CR_TRACE fprintf(stdout, "CR_TRACE: %sn", __FUNCTION__);
# else
# define CR_TRACE
# endif
#endif
#ifndef CR_MAIN_FUNC
# define CR_MAIN_FUNC "cr_main"
#endif
#ifndef CR_ASSERT
# include
# define CR_ASSERT ( e ) assert(e)
#endif
#ifndef CR_REALLOC
# include
# define CR_REALLOC ( ptr , size ) ::realloc(ptr, size)
#endif
#ifndef CR_FREE
# include
# define CR_FREE ( ptr ) ::free(ptr)
#endif
#ifndef CR_MALLOC
# include
# define CR_MALLOC ( size ) ::malloc(size)
#endif
#if defined( _MSC_VER )
// we should probably push and pop this
# pragma warning(disable:4003) // not enough actual parameters for macro 'identifier'
#endif
#define CR_DO_EXPAND ( x ) x##1337
#define CR_EXPAND ( x ) CR_DO_EXPAND(x)
#if CR_EXPAND ( CR_HOST ) == 1337
#define CR_OP_MODE CR_UNSAFE
#else
#define CR_OP_MODE CR_HOST
#endif
#include
#include // duration for sleep
#include // memcpy
#include
#include // this_thread::sleep_for
#if defined( CR_WINDOWS )
#define CR_PATH_SEPARATOR '\'
#define CR_PATH_SEPARATOR_INVALID '/'
#else
#define CR_PATH_SEPARATOR '/'
#define CR_PATH_SEPARATOR_INVALID '\'
#endif
static void cr_split_path ( std :: string path , std :: string & parent_dir ,
std :: string & base_name , std :: string & ext ) {
std:: replace ( path . begin (), path . end (), CR_PATH_SEPARATOR_INVALID ,
CR_PATH_SEPARATOR );
auto sep_pos = path . rfind ( CR_PATH_SEPARATOR );
auto dot_pos = path . rfind ( '.' );
if ( sep_pos == std :: string :: npos ) {
parent_dir = "" ;
if ( dot_pos == std :: string :: npos ) {
ext = "" ;
base_name = path ;
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( 0 , dot_pos );
}
} else {
parent_dir = path . substr ( 0 , sep_pos + 1 );
if ( dot_pos == std :: string :: npos || sep_pos > dot_pos ) {
ext = "" ;
base_name = path . substr ( sep_pos + 1 );
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( sep_pos + 1 , dot_pos - sep_pos - 1 );
}
}
}
static std :: string cr_version_path ( const std :: string & basepath ,
unsigned version ,
const std :: string & temppath ) {
std:: string folder , fname , ext ;
cr_split_path ( basepath , folder , fname , ext );
std:: string ver = std :: to_string ( version );
#if defined( _MSC_VER )
// When patching PDB file path in library file we will drop path and leave only file name.
// Length of path is extra space for version number. Trim file name only if version number
// length exceeds pdb folder path length. This is not relevant on other platforms.
if ( ver . size () > folder . size ()) {
fname = fname . substr ( 0 , fname . size () - ( ver . size () - folder . size () - 1 ));
}
#endif
if (! temppath . empty ()) {
folder = temppath ;
}
return folder + fname + ver + ext ;
}
namespace cr_plugin_section_type {
enum e { state , bss , count };
}
namespace cr_plugin_section_version {
enum e { backup , current , count };
}
struct cr_plugin_section {
cr_plugin_section_type :: e type = {};
intptr_t base = 0 ;
char * ptr = 0 ;
int64_t size = 0 ;
void * data = nullptr ;
};
struct cr_plugin_segment {
char * ptr = 0 ;
int64_t size = 0 ;
};
// keep track of some internal state about the plugin, should not be messed
// with by user
struct cr_internal {
std :: string fullname = {};
std:: string temppath = {};
time_t timestamp = {};
void * handle = nullptr ;
cr_plugin_main_func main = nullptr ;
cr_plugin_segment seg = {};
cr_plugin_section data [ cr_plugin_section_type :: count ]
[ cr_plugin_section_version :: count ] = {};
cr_mode mode = CR_SAFEST ;
};
static bool cr_plugin_section_validate ( cr_plugin & ctx ,
cr_plugin_section_type :: e type ,
intptr_t vaddr , intptr_t ptr ,
int64_t size );
static void cr_plugin_sections_reload ( cr_plugin & ctx ,
cr_plugin_section_version :: e version );
static void cr_plugin_sections_store ( cr_plugin & ctx );
static void cr_plugin_sections_backup ( cr_plugin & ctx );
static void cr_plugin_reload ( cr_plugin & ctx );
static int cr_plugin_unload ( cr_plugin & ctx , bool rollback , bool close );
static bool cr_plugin_changed ( cr_plugin & ctx );
static bool cr_plugin_rollback ( cr_plugin & ctx );
static int cr_plugin_main ( cr_plugin & ctx , cr_op operation );
void cr_set_temporary_path ( cr_plugin & ctx , const std :: string & path ) {
auto pimpl = ( cr_internal * ) ctx . p ;
pimpl -> temppath = path ;
}
#if defined( CR_WINDOWS )
// clang-format off
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include
#include
// clang-format on
#if defined( _MSC_VER )
#pragma comment(lib, "dbghelp.lib")
#endif
using so_handle = HMODULE ;
#ifdef UNICODE
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) std::wstring _newpath(cr_utf8_to_wstring(_path))
static std :: wstring cr_utf8_to_wstring ( const std :: string & str ) {
int wlen = MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , 0 , 0 );
wchar_t wpath_small [ MAX_PATH ];
std:: unique_ptr < wchar_t [] > wpath_big ;
wchar_t * wpath = wpath_small ;
if ( wlen > _countof ( wpath_small )) {
wpath_big = std :: unique_ptr < wchar_t [] > ( new wchar_t [ wlen ]);
wpath = wpath_big . get ();
}
if ( MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , wpath , wlen ) != wlen ) {
return L"" ;
}
return wpath ;
}
#else
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) const std::string &_newpath = _path
#endif // UNICODE
static time_t cr_last_write_time ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
WIN32_FILE_ATTRIBUTE_DATA fad ;
if (! GetFileAttributesEx ( _path . c_str (), GetFileExInfoStandard , & fad )) {
return -1 ;
}
if ( fad . nFileSizeHigh == 0 && fad . nFileSizeLow == 0 ) {
return -1 ;
}
LARGE_INTEGER time ;
time . HighPart = fad . ftLastWriteTime . dwHighDateTime ;
time . LowPart = fad . ftLastWriteTime . dwLowDateTime ;
return static_cast < time_t > ( time . QuadPart / 10000000 - 11644473600LL );
}
static bool cr_exists ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
return GetFileAttributes ( _path . c_str ()) != INVALID_FILE_ATTRIBUTES ;
}
static bool cr_copy ( const std :: string & from , const std :: string & to ) {
CR_WINDOWS_ConvertPath ( _from , from );
CR_WINDOWS_ConvertPath ( _to , to );
return CopyFile ( _from . c_str (), _to . c_str (), FALSE) ? true : false;
}
static void cr_del ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
DeleteFile ( _path . c_str ());
}
// If using Microsoft Visual C/C++ compiler we need to do some workaround the
// fact that the compiled binary has a fullpath to the PDB hardcoded inside
// it. This causes a lot of headaches when trying compile while debugging as
// the referenced PDB will be locked by the debugger.
// To solve this problem, we patch the binary to rename the PDB to something
// we know will be unique to our in-flight instance, so when debugging it will
// lock this unique PDB and the compiler will be able to overwrite the
// original one.
#if defined( _MSC_VER )
#include
#include
#include
#include
static std :: string cr_replace_extension ( const std :: string & filepath ,
const std :: string & ext ) {
std:: string folder , filename , old_ext ;
cr_split_path ( filepath , folder , filename , old_ext );
return folder + filename + ext ;
}
template < class T >
static T struct_cast ( void * ptr , LONG offset = 0 ) {
return reinterpret_cast < T > ( reinterpret_cast < intptr_t > ( ptr ) + offset );
}
// RSDS Debug Information for PDB files
using DebugInfoSignature = DWORD ;
#define CR_RSDS_SIGNATURE 'SDSR'
struct cr_rsds_hdr {
DebugInfoSignature signature ;
GUID guid ;
long version ;
char filename [ 1 ];
};
static bool cr_pe_debugdir_rva ( PIMAGE_OPTIONAL_HEADER optionalHeader ,
DWORD & debugDirRva , DWORD & debugDirSize ) {
if ( optionalHeader -> Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC ) {
auto optionalHeader64 =
struct_cast < PIMAGE_OPTIONAL_HEADER64 > ( optionalHeader );
debugDirRva =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]
. VirtualAddress ;
debugDirSize =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]. Size ;
} else {
auto optionalHeader32 =
struct_cast < PIMAGE_OPTIONAL_HEADER32 > ( optionalHeader );
debugDirRva =
optionalHeader32 -> DataDirectory [ IMAGE_DIRECTO