Skip to content

Commit 7548415

Browse files
maximecbXrXr
andcommitted
Try to alloc executable memory within rel32 range on Linux machines (#12)
* Use INT32_MIN, INT32_MAX, etc. constants in yjit_asm.c * Print warning on stderr when code past rel32 jump range * Fix preprocessor snafu * Move rel32 warning into --yjit-stats * Try to allocate within rel32 offset on Linux machines * Update yjit_asm.c Co-authored-by: Alan Wu <[email protected]> * On Linux, use sysconf to get the page size Co-authored-by: Alan Wu <[email protected]>
1 parent 4a9594a commit 7548415

File tree

2 files changed

+82
-26
lines changed

2 files changed

+82
-26
lines changed

yjit_asm.c

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
#include <stdlib.h>
66
#include <string.h>
77
#include <stdarg.h>
8+
#include <stdint.h>
89
#include <assert.h>
910

11+
// For mmapp(), sysconf()
1012
#ifndef _WIN32
11-
// For mmapp()
13+
#include <unistd.h>
1214
#include <sys/mman.h>
1315
#endif
1416

@@ -18,11 +20,11 @@
1820
uint32_t sig_imm_size(int64_t imm)
1921
{
2022
// Compute the smallest size this immediate fits in
21-
if (imm >= -128 && imm <= 127)
23+
if (imm >= INT8_MIN && imm <= INT8_MAX)
2224
return 8;
23-
if (imm >= -32768 && imm <= 32767)
25+
if (imm >= INT16_MIN && imm <= INT16_MAX)
2426
return 16;
25-
if (imm >= -2147483648 && imm <= 2147483647)
27+
if (imm >= INT32_MIN && imm <= INT32_MAX)
2628
return 32;
2729

2830
return 64;
@@ -32,11 +34,11 @@ uint32_t sig_imm_size(int64_t imm)
3234
uint32_t unsig_imm_size(uint64_t imm)
3335
{
3436
// Compute the smallest size this immediate fits in
35-
if (imm <= 255)
37+
if (imm <= UINT8_MAX)
3638
return 8;
37-
else if (imm <= 65535)
39+
else if (imm <= UINT16_MAX)
3840
return 16;
39-
else if (imm <= 4294967295)
41+
else if (imm <= UINT32_MAX)
4042
return 32;
4143

4244
return 64;
@@ -124,23 +126,73 @@ x86opnd_t const_ptr_opnd(const void *ptr)
124126
return opnd;
125127
}
126128

129+
// Align the current write position to a multiple of bytes
130+
static uint8_t* align_ptr(uint8_t* ptr, uint32_t multiple)
131+
{
132+
// Compute the pointer modulo the given alignment boundary
133+
uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
134+
135+
// If the pointer is already aligned, stop
136+
if (rem == 0)
137+
return ptr;
138+
139+
// Pad the pointer by the necessary amount to align it
140+
uint32_t pad = multiple - rem;
141+
142+
return ptr + pad;
143+
}
144+
127145
// Allocate a block of executable memory
128146
uint8_t* alloc_exec_mem(uint32_t mem_size)
129147
{
130148
#ifndef _WIN32
131-
// Map the memory as executable
132-
uint8_t* mem_block = (uint8_t*)mmap(
133-
(void*)&alloc_exec_mem,
134-
mem_size,
135-
PROT_READ | PROT_WRITE | PROT_EXEC,
136-
MAP_PRIVATE | MAP_ANONYMOUS,
137-
-1,
138-
0
139-
);
149+
uint8_t* mem_block;
150+
151+
// On Linux
152+
#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
153+
// Align the requested address to page size
154+
uint32_t page_size = (uint32_t)sysconf(_SC_PAGESIZE);
155+
uint8_t* req_addr = align_ptr((uint8_t*)&alloc_exec_mem, page_size);
156+
157+
while (req_addr < (uint8_t*)&alloc_exec_mem + INT32_MAX)
158+
{
159+
// Try to map a chunk of memory as executable
160+
mem_block = (uint8_t*)mmap(
161+
(void*)req_addr,
162+
mem_size,
163+
PROT_READ | PROT_WRITE | PROT_EXEC,
164+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
165+
-1,
166+
0
167+
);
140168

169+
// If we succeeded, stop
170+
if (mem_block != MAP_FAILED) {
171+
break;
172+
}
173+
174+
// +4MB
175+
req_addr += 4 * 1024 * 1024;
176+
}
177+
178+
// On MacOS and other platforms
179+
#else
180+
// Try to map a chunk of memory as executable
181+
mem_block = (uint8_t*)mmap(
182+
(void*)alloc_exec_mem,
183+
mem_size,
184+
PROT_READ | PROT_WRITE | PROT_EXEC,
185+
MAP_PRIVATE | MAP_ANONYMOUS,
186+
-1,
187+
0
188+
);
189+
#endif
190+
191+
// Fallback
141192
if (mem_block == MAP_FAILED) {
193+
// Try again without the address hint (e.g., valgrind)
142194
mem_block = (uint8_t*)mmap(
143-
NULL, // try again without the address hint (e.g., valgrind)
195+
NULL,
144196
mem_size,
145197
PROT_READ | PROT_WRITE | PROT_EXEC,
146198
MAP_PRIVATE | MAP_ANONYMOUS,
@@ -161,6 +213,7 @@ uint8_t* alloc_exec_mem(uint32_t mem_size)
161213

162214
return mem_block;
163215
#else
216+
// Windows not supported for now
164217
return NULL;
165218
#endif
166219
}
@@ -180,15 +233,11 @@ void cb_align_pos(codeblock_t* cb, uint32_t multiple)
180233
{
181234
// Compute the pointer modulo the given alignment boundary
182235
uint8_t* ptr = &cb->mem_block[cb->write_pos];
183-
uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
184-
185-
// If the pointer is already aligned, stop
186-
if (rem == 0)
187-
return;
236+
uint8_t* aligned_ptr = align_ptr(ptr, multiple);
188237

189238
// Pad the pointer by the necessary amount to align it
190-
uint32_t pad = multiple - rem;
191-
cb->write_pos += pad;
239+
ptrdiff_t pad = aligned_ptr - ptr;
240+
cb->write_pos += (int32_t)pad;
192241
}
193242

194243
// Set the current write position
@@ -818,7 +867,7 @@ void cb_write_jcc_ptr(codeblock_t* cb, const char* mnem, uint8_t op0, uint8_t op
818867

819868
// Compute the jump offset
820869
int64_t rel64 = (int64_t)(dst_ptr - end_ptr);
821-
assert (rel64 >= -2147483648 && rel64 <= 2147483647);
870+
assert (rel64 >= INT32_MIN && rel64 <= INT32_MAX);
822871

823872
// Write the relative 32-bit jump offset
824873
cb_write_int(cb, (int32_t)rel64, 32);
@@ -901,7 +950,7 @@ void call_ptr(codeblock_t* cb, x86opnd_t scratch_reg, uint8_t* dst_ptr)
901950
int64_t rel64 = (int64_t)(dst_ptr - end_ptr);
902951

903952
// If the offset fits in 32-bit
904-
if (rel64 >= -2147483648 && rel64 <= 2147483647)
953+
if (rel64 >= INT32_MIN && rel64 <= INT32_MAX)
905954
{
906955
call_rel32(cb, (int32_t)rel64);
907956
return;

yjit_iface.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,13 @@ print_yjit_stats(void)
896896
return;
897897
}
898898

899+
// Warn if the executable code block is out of the relative
900+
// 32-bit jump range away from compiled C code
901+
ptrdiff_t start_diff = (cb->mem_block + cb->mem_size) - (uint8_t*)&print_yjit_stats;
902+
if (start_diff < INT32_MIN || start_diff > INT32_MAX) {
903+
fprintf(stderr, "WARNING: end of code block past rel32 offset range from C code\n");
904+
}
905+
899906
// Compute the total exit count
900907
int64_t total_exit_count = calc_total_exit_count();
901908

0 commit comments

Comments
 (0)