8000 Mark JIT code as writeable / executable depending on the situation · ruby/ruby@0e60bdb · GitHub
[go: up one dir, main page]

Skip to content

Commit 0e60bdb

Browse files
committed
Mark JIT code as writeable / executable depending on the situation
Some platforms don't want memory to be marked as writeable and executable at the same time. This commit introduces two functions for marking code blocks as either writeable or executable depending on the situation. When we need to write to memory, we call `cb_mark_writeable` and when we're done writing to memory, call `cb_mark_executable`
1 parent e5319dc commit 0e60bdb

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

yjit_asm.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,20 @@ static uint8_t *align_ptr(uint8_t *ptr, uint32_t multiple)
146146
return ptr + pad;
147147
}
148148

149+
void cb_mark_writeable(codeblock_t *cb)
150+
{
151+
if (mprotect(cb->mem_block, cb->mem_size, PROT_READ | PROT_WRITE)) {
152+
rb_bug("Couldn't make JIT page (%p) writeable, errno: %s", (void *)cb->mem_block, strerror(errno));
153+
}
154+
}
155+
156+
void cb_mark_executable(codeblock_t *cb)
157+
{
158+
if (mprotect(cb->mem_block, cb->mem_size, PROT_READ | PROT_EXEC)) {
159+
rb_bug("Couldn't make JIT page (%p) executable, errno: %s", (void *)cb->mem_block, strerror(errno));
160+
}
161+
}
162+
149163
// Allocate a block of executable memory
150164
uint8_t *alloc_exec_mem(uint32_t mem_size)
151165
{
@@ -163,7 +177,7 @@ uint8_t *alloc_exec_mem(uint32_t mem_size)
163177
mem_block = (uint8_t*)mmap(
164178
(void*)req_addr,
165179
mem_size,
166-
PROT_READ | PROT_WRITE | PROT_EXEC,
180+
PROT_READ | PROT_EXEC,
167181
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
168182
-1,
169183
0
@@ -184,7 +198,7 @@ uint8_t *alloc_exec_mem(uint32_t mem_size)
184198
mem_block = (uint8_t*)mmap(
185199
(void*)alloc_exec_mem,
186200
mem_size,
187-
PROT_READ | PROT_WRITE | PROT_EXEC,
201+
PROT_READ | PROT_EXEC,
188202
MAP_PRIVATE | MAP_ANONYMOUS,
189203
-1,
190204
0
@@ -197,7 +211,7 @@ uint8_t *alloc_exec_mem(uint32_t mem_size)
197211
mem_block = (uint8_t*)mmap(
198212
NULL,
199213
mem_size,
200-
PROT_READ | PROT_WRITE | PROT_EXEC,
214+
PROT_READ | PROT_EXEC,
201215
MAP_PRIVATE | MAP_ANONYMOUS,
202216
-1,
203217
0
@@ -210,9 +224,14 @@ uint8_t *alloc_exec_mem(uint32_t mem_size)
210224
exit(-1);
211225
}
212226

227+
codeblock_t block;
228+
block.mem_block = mem_block;
229+
block.mem_size = mem_size;
213230
// Fill the executable memory with INT3 (0xCC) so that
214231
// executing uninitialized memory will fault
232+
cb_mark_writeable(&block);
215233
memset(mem_block, 0xCC, mem_size);
234+
cb_mark_executable(&block);
216235

217236
return mem_block;
218237
#else

yjit_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ static inline uint32_t cb_new_label(codeblock_t *cb, const char *name);
283283
static inline void cb_write_label(codeblock_t *cb, uint32_t label_idx);
284284
static inline void cb_label_ref(codeblock_t *cb, uint32_t label_idx);
285285
static inline void cb_link_labels(codeblock_t *cb);
286+
void cb_mark_writeable(codeblock_t *cb);
287+
void cb_mark_executable(codeblock_t *cb);
286288

287289
// Encode individual instructions into a code block
288290
static inline void add(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);

yjit_codegen.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ gen_full_cfunc_return(void)
475475
codeblock_t *cb = ocb;
476476
outline_full_cfunc_return_pos = ocb->write_pos;
477477

478+
cb_mark_writeable(cb);
479+
478480
// This chunk of code expect REG_EC to be filled properly and
479481
// RAX to contain the return value of the C method.
480482

@@ -493,6 +495,7 @@ gen_full_cfunc_return(void)
493495

494496
mov(cb, RAX, imm_opnd(Qundef));
495497
ret(cb);
498+
cb_mark_executable(cb);
496499
}
497500

498501
/*
@@ -4638,16 +4641,22 @@ yjit_init_codegen(void)
46384641
uint8_t *mem_block = alloc_exec_mem(mem_size);
46394642

46404643
cb = █
4644+
cb_mark_writeable(cb);
46414645
cb_init(cb, mem_block, mem_size/2);
4646+
cb_mark_executable(cb);
46424647

46434648
ocb = &outline_block;
4649+
cb_mark_writeable(ocb);
46444650
cb_init(ocb, mem_block + mem_size/2, mem_size/2);
4651+
cb_mark_executable(ocb);
46454652

46464653
// Generate the interpreter exit code for leave
4654+
cb_mark_writeable(cb);
46474655
leave_exit_code = yjit_gen_leave_exit(cb);
46484656

46494657
// Generate full exit code for C func
46504658
gen_full_cfunc_return();
4659+
cb_mark_writeable(cb);
46514660

46524661
// Map YARV opcodes to the corresponding codegen functions
46534662
yjit_reg_op(BIN(nop), gen_nop);

yjit_core.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,12 +739,17 @@ gen_entry_point(const rb_iseq_t *iseq, uint32_t insn_idx, rb_execution_context_t
739739
// The entry context makes no assumptions about types
740740
blockid_t blockid = { iseq, insn_idx };
741741

742+
cb_mark_writeable(cb);
743+
cb_mark_writeable(ocb);
742744
// Write the interpreter entry prologue
743745
uint8_t *code_ptr = yjit_entry_prologue(cb, iseq);
744746

745747
// Try to generate code for the entry block
746748
block_t *block = gen_block_version(blockid, &DEFAULT_CTX, ec);
747749

750+
cb_mark_executable(ocb);
751+
cb_mark_executable(cb);
752+
748753
// If we couldn't generate any code
749754
if (block->end_idx == insn_idx)
750755
{
@@ -778,6 +783,8 @@ branch_stub_hit(branch_t *branch, const uint32_t target_idx, rb_execution_contex
778783
}
779784
else
780785
{
786+
cb_mark_writeable(cb);
787+
cb_mark_writeable(ocb);
781788
//fprintf(stderr, "\nstub hit, branch: %p, target idx: %d\n", branch, target_idx);
782789
//fprintf(stderr, "blockid.iseq=%p, blockid.idx=%d\n", target.iseq, target.idx);
783790
//fprintf(stderr, "chain_depth=%d\n", target_ctx->chain_depth);
@@ -844,6 +851,9 @@ branch_stub_hit(branch_t *branch, const uint32_t target_idx, rb_execution_contex
844851

845852
// Restore interpreter sp, since the code hitting the stub expects the original.
846853
ec->cfp->sp = original_interp_sp;
854+
855+
cb_mark_executable(ocb);
856+
cb_mark_executable(cb);
847857
}
848858

849859
RB_VM_LOCK_LEAVE();
@@ -1083,6 +1093,9 @@ static void
10831093
invalidate_block_version(block_t *block)
10841094
{
10851095
ASSERT_vm_locking();
1096+
cb_mark_writeable(cb);
1097+
cb_mark_writeable(ocb);
1098+
10861099
// TODO: want to assert that all other ractors are stopped here. Can't patch
10871100
// machine code that some other thread is running.
10881101

@@ -1176,6 +1189,9 @@ invalidate_block_version(block_t *block)
11761189
yjit_runtime_counters.invalidation_count++;
11771190
#endif
11781191

1192+
cb_mark_executable(ocb);
1193+
cb_mark_executable(cb);
1194+
11791195
// fprintf(stderr, "invalidation done\n");
11801196
}
11811197

0 commit comments

Comments
 (0)
0