runtime_loader: Actually respect PF_EXECUTE program header.

If it's not set (and we aren't on pre-Haiku GCC2 ABI), then don't make
the region executable.

This mostly only makes a difference for Clang/LLD-linked binaries
that have a readable, non-writable, non-executable program segment.
GCC/LD-linked binaries just have all read-only data in the main
read+execute section by default.
This commit is contained in:
Augustin Cavalier 2025-01-14 22:15:59 -05:00
parent e0101fa39b
commit e38be9392a
3 changed files with 37 additions and 29 deletions

View File

@ -120,6 +120,17 @@ parse_program_headers(image_t* image, char* buff, int phnum, int phentsize)
/* NOP header */
break;
case PT_LOAD:
{
uint32 flags = 0;
if (pheader->p_flags & PF_WRITE) {
// this is a writable segment
flags |= RFLAG_WRITABLE;
}
if (pheader->p_flags & PF_EXECUTE) {
// this is an executable segment
flags |= RFLAG_EXECUTABLE;
}
if (pheader->p_memsz == pheader->p_filesz) {
/*
* everything in one area
@ -134,11 +145,7 @@ parse_program_headers(image_t* image, char* buff, int phnum, int phentsize)
image->regions[regcount].fdstart = pheader->p_offset;
image->regions[regcount].fdsize = pheader->p_filesz;
image->regions[regcount].delta = 0;
image->regions[regcount].flags = 0;
if (pheader->p_flags & PF_WRITE) {
// this is a writable segment
image->regions[regcount].flags |= RFLAG_RW;
}
image->regions[regcount].flags = flags;
} else {
/*
* may require splitting
@ -158,11 +165,7 @@ parse_program_headers(image_t* image, char* buff, int phnum, int phentsize)
image->regions[regcount].fdstart = pheader->p_offset;
image->regions[regcount].fdsize = pheader->p_filesz;
image->regions[regcount].delta = 0;
image->regions[regcount].flags = 0;
if (pheader->p_flags & PF_WRITE) {
// this is a writable segment
image->regions[regcount].flags |= RFLAG_RW;
}
image->regions[regcount].flags = flags;
if (A != B) {
/*
@ -182,15 +185,12 @@ parse_program_headers(image_t* image, char* buff, int phnum, int phentsize)
image->regions[regcount].fdstart = 0;
image->regions[regcount].fdsize = 0;
image->regions[regcount].delta = 0;
image->regions[regcount].flags = RFLAG_ANON;
if (pheader->p_flags & PF_WRITE) {
// this is a writable segment
image->regions[regcount].flags |= RFLAG_RW;
}
image->regions[regcount].flags = flags | RFLAG_ANON;
}
}
regcount += 1;
break;
}
case PT_DYNAMIC:
image->dynamic_ptr = pheader->p_vaddr;
break;

View File

@ -348,9 +348,10 @@ map_image(int fd, char const* path, image_t* image, bool fixed)
for (uint32 i = 0; i < image->num_regions; i++) {
char regionName[B_OS_NAME_LENGTH];
snprintf(regionName, sizeof(regionName), "%s_seg%" B_PRIu32 "%s",
baseName, i, (image->regions[i].flags & RFLAG_RW) ? "rw" : "ro");
baseName, i, (image->regions[i].flags & RFLAG_EXECUTABLE) ?
((image->regions[i].flags & RFLAG_WRITABLE) ? "rwx" : "rx")
: (image->regions[i].flags & RFLAG_WRITABLE) ? "rw" : "ro");
get_image_region_load_address(image, i,
i > 0 ? image->regions[i - 1].delta : 0, fixed, loadAddress,
@ -380,7 +381,7 @@ map_image(int fd, char const* path, image_t* image, bool fixed)
// of memory to be committed for them temporarily, just because we
// have to write map them.
uint32 protection = B_READ_AREA | B_WRITE_AREA
| ((image->regions[i].flags & RFLAG_RW) != 0
| ((image->regions[i].flags & RFLAG_WRITABLE) != 0
? 0 : B_OVERCOMMITTING_AREA);
image->regions[i].id = _kern_map_file(regionName,
(void**)&loadAddress, B_EXACT_ADDRESS,
@ -394,10 +395,10 @@ map_image(int fd, char const* path, image_t* image, bool fixed)
TRACE(("\"%s\" at %p, 0x%lx bytes (%s)\n", path,
(void *)loadAddress, image->regions[i].vmsize,
image->regions[i].flags & RFLAG_RW ? "rw" : "read-only"));
image->regions[i].flags & RFLAG_WRITABLE ? "rw" : "read-only"));
// handle trailer bits in data segment
if (image->regions[i].flags & RFLAG_RW) {
if (image->regions[i].flags & RFLAG_WRITABLE) {
addr_t startClearing = loadAddress
+ PAGE_OFFSET(image->regions[i].start)
+ image->regions[i].size;
@ -437,11 +438,11 @@ unmap_image(image_t* image)
}
/*! This function will change the protection of all read-only segments to really
be read-only (and executable).
/*! This function will change the protection of all segments to what they're
supposed to be.
The areas have to be read/write first, so that they can be relocated.
If at least one image is in compatibility mode then we allow execution of
If at least one image is in compatibility mode then we allow write/execute of
all areas.
*/
void
@ -455,9 +456,15 @@ remap_images()
if ((region.flags & RFLAG_REMAPPED) != 0)
continue;
uint32 protection = B_READ_AREA | B_EXECUTE_AREA;
if ((region.flags & RFLAG_RW) != 0 || image->abi < B_HAIKU_ABI_GCC_2_HAIKU)
uint32 protection = B_READ_AREA;
if (image->abi < B_HAIKU_ABI_GCC_2_HAIKU) {
protection |= B_WRITE_AREA | B_EXECUTE_AREA;
} else {
if ((region.flags & RFLAG_WRITABLE) != 0)
protection |= B_WRITE_AREA;
if ((region.flags & RFLAG_EXECUTABLE) != 0)
protection |= B_EXECUTE_AREA;
}
status_t result = _kern_set_area_protection(region.id, protection);
if (result == B_OK)
@ -498,7 +505,7 @@ register_image(image_t* image, int fd, const char* path)
for (uint32 i= 0; i < image->num_regions; i++) {
addr_t base = image->regions[i].vmstart;
addr_t end = base + image->regions[i].vmsize;
if (image->regions[i].flags & RFLAG_RW) {
if (image->regions[i].flags & RFLAG_WRITABLE) {
// data
if (dataBase == 0) {
dataBase = base;

View File

@ -16,8 +16,9 @@
enum {
// the lower two bits are reserved for RTLD_NOW and RTLD_GLOBAL
RFLAG_RW = 0x0010,
RFLAG_ANON = 0x0020,
RFLAG_WRITABLE = 0x0010,
RFLAG_EXECUTABLE = 0x0020,
RFLAG_ANON = 0x0040,
RFLAG_TERMINATED = 0x0200,
RFLAG_INITIALIZED = 0x0400,