|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* libhugetlbfs - Easy use of Linux hugepages
|
|
Packit |
2d622a |
* Copyright (C) 2008 Adam Litke, IBM Corporation.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* This program is free software; you can redistribute it and/or
|
|
Packit |
2d622a |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
2d622a |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
2d622a |
* the License, or (at your option) any later version.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
2d622a |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
2d622a |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
2d622a |
* Lesser General Public License for more details.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
2d622a |
* License along with this library; if not, write to the Free Software
|
|
Packit |
2d622a |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#define _GNU_SOURCE
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#include <stdio.h>
|
|
Packit |
2d622a |
#include <stdlib.h>
|
|
Packit |
2d622a |
#include <string.h>
|
|
Packit |
2d622a |
#include <unistd.h>
|
|
Packit |
2d622a |
#include <fcntl.h>
|
|
Packit |
2d622a |
#include <elf.h>
|
|
Packit |
2d622a |
#include <link.h>
|
|
Packit |
2d622a |
#include <getopt.h>
|
|
Packit |
2d622a |
#include <errno.h>
|
|
Packit |
2d622a |
#include <sys/stat.h>
|
|
Packit |
2d622a |
#include <sys/mman.h>
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* Eventually we plan to use the libhugetlbfs reporting facility,
|
|
Packit |
2d622a |
* but until that is possible, redefine a simpler version here.
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
#define REPORT(level, prefix, format, ...) \
|
|
Packit |
2d622a |
do { \
|
|
Packit |
2d622a |
fprintf(stderr, "hugeedit: " prefix ": " format, \
|
|
Packit |
2d622a |
##__VA_ARGS__); \
|
|
Packit |
2d622a |
} while (0)
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#include "libhugetlbfs_internal.h"
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* All MAP_* options are tagged with MAP_BASE to differentiate them as options
|
|
Packit |
2d622a |
* in the options parser. This must be removed before they are compared.
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
#define MAP_BASE 0x1000
|
|
Packit |
2d622a |
#define MAP_DISABLE 0x0001
|
|
Packit |
2d622a |
#define MAP_TEXT 0x0002
|
|
Packit |
2d622a |
#define MAP_DATA 0x0004
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#define PF_LINUX_HUGETLB 0x100000
|
|
Packit |
2d622a |
extern int optind;
|
|
Packit |
2d622a |
extern char *optarg;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#define OPTION(opts, text) fprintf(stderr, " %-25s %s\n", opts, text)
|
|
Packit |
2d622a |
#define CONT(text) fprintf(stderr, " %-25s %s\n", "", text)
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
void print_usage()
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
fprintf(stderr, "hugeedit [options] target\n");
|
|
Packit |
2d622a |
fprintf(stderr, "options:\n");
|
|
Packit |
2d622a |
OPTION("--text", "Remap program text into huge pages by default");
|
|
Packit |
2d622a |
OPTION("--data", "Remap program data into huge pages by default");
|
|
Packit |
2d622a |
OPTION("--disable", "Remap no segments into huge pages by default");
|
|
Packit |
2d622a |
OPTION("--help, -h", "Print this usage information");
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
int check_elf_wordsize(void *ehdr)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
char *e_ident = (char *) ehdr;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (strncmp(e_ident, ELFMAG, SELFMAG)) {
|
|
Packit |
2d622a |
ERROR("Not a valid ELF executable\n");
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
switch (e_ident[EI_CLASS]) {
|
|
Packit |
2d622a |
case ELFCLASS32:
|
|
Packit |
2d622a |
case ELFCLASS64:
|
|
Packit |
2d622a |
return e_ident[EI_CLASS];
|
|
Packit |
2d622a |
default:
|
|
Packit |
2d622a |
ERROR("Can not determine word size\n");
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* We need to map enough of the binary so that we can access the ELF header and
|
|
Packit |
2d622a |
* all of the program headers. This function takes a pointer to the first page
|
|
Packit |
2d622a |
* of ELF headers which is guaranteed to be enough data to determine if we need
|
|
Packit |
2d622a |
* to map more of the binary. Use mremap to enlarge the mapping if needed.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* void **elf - may be updated with a new address if mremap moved it
|
|
Packit |
2d622a |
* unsigned long *size - may be updated with the new mapping size
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
#define elf_ph_end_offset(e) ((e)->e_phoff + (e)->e_phentsize * (e)->e_phnum)
|
|
Packit |
2d622a |
void check_remap_elf(void **elf, unsigned long *size, int wordsize)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
unsigned long newsize;
|
|
Packit |
2d622a |
int pagesize = getpagesize();
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (wordsize == ELFCLASS32) {
|
|
Packit |
2d622a |
Elf32_Ehdr *ehdr = *(Elf32_Ehdr **) elf;
|
|
Packit |
2d622a |
newsize = elf_ph_end_offset(ehdr);
|
|
Packit |
2d622a |
} else {
|
|
Packit |
2d622a |
Elf64_Ehdr *ehdr = *(Elf64_Ehdr **) elf;
|
|
Packit |
2d622a |
newsize = elf_ph_end_offset(ehdr);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
newsize = ALIGN_UP(newsize, pagesize);
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (newsize > *size) {
|
|
Packit |
2d622a |
*size = newsize;
|
|
Packit |
2d622a |
*elf = mremap(*elf, *size, newsize, MREMAP_MAYMOVE);
|
|
Packit |
2d622a |
if (*elf == MAP_FAILED) {
|
|
Packit |
2d622a |
ERROR("Remapping failed: %s\n", strerror(errno));
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#define is_text(p) ((((p)->p_flags & (PF_R|PF_W|PF_X)) == (PF_R|PF_X)) && \
|
|
Packit |
2d622a |
((p)->p_memsz == (p)->p_filesz))
|
|
Packit |
2d622a |
#if defined(__powerpc__) && !defined(__powerpc64__)
|
|
Packit |
2d622a |
#define is_data(p) (((p)->p_flags & (PF_R|PF_W|PF_X)) == (PF_R|PF_W|PF_X))
|
|
Packit |
2d622a |
#else
|
|
Packit |
2d622a |
#define is_data(p) (((p)->p_flags & (PF_R|PF_W|PF_X)) == (PF_R|PF_W))
|
|
Packit |
2d622a |
#endif
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#define update_phdrs(_BITS_) \
|
|
Packit |
2d622a |
void update_phdrs##_BITS_(Elf##_BITS_##_Ehdr *ehdr, int remap_opts) \
|
|
Packit |
2d622a |
{ \
|
|
Packit |
2d622a |
int i; \
|
|
Packit |
2d622a |
Elf##_BITS_##_Phdr *phdr; \
|
|
Packit |
2d622a |
unsigned long long start, end; \
|
|
Packit |
2d622a |
\
|
|
Packit |
2d622a |
phdr = (Elf##_BITS_##_Phdr *)((char *)ehdr + ehdr->e_phoff); \
|
|
Packit |
2d622a |
for (i = 0; i < ehdr->e_phnum; i++) { \
|
|
Packit |
2d622a |
if (phdr[i].p_type != PT_LOAD) \
|
|
Packit |
2d622a |
continue; \
|
|
Packit |
2d622a |
if (remap_opts) \
|
|
Packit |
2d622a |
phdr[i].p_flags &= ~PF_LINUX_HUGETLB; \
|
|
Packit |
2d622a |
if ((remap_opts & MAP_TEXT) && is_text(&phdr[i])) \
|
|
Packit |
2d622a |
phdr[i].p_flags |= PF_LINUX_HUGETLB; \
|
|
Packit |
2d622a |
if ((remap_opts & MAP_DATA) && is_data(&phdr[i])) \
|
|
Packit |
2d622a |
phdr[i].p_flags |= PF_LINUX_HUGETLB; \
|
|
Packit |
2d622a |
start = (unsigned long long) phdr[i].p_vaddr; \
|
|
Packit |
2d622a |
end = start + phdr[i].p_memsz; \
|
|
Packit |
2d622a |
printf("Segment %i 0x%llx - 0x%llx (%s%s) default is " \
|
|
Packit |
2d622a |
"%s pages\n", i, start, end, \
|
|
Packit |
2d622a |
is_text(&phdr[i]) ? "TEXT" : "", \
|
|
Packit |
2d622a |
is_data(&phdr[i]) ? "DATA" : "", \
|
|
Packit |
2d622a |
(phdr[i].p_flags & PF_LINUX_HUGETLB) ? \
|
|
Packit |
2d622a |
"HUGE" : "BASE"); \
|
|
Packit |
2d622a |
} \
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
update_phdrs(32)
|
|
Packit |
2d622a |
update_phdrs(64)
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
int main(int argc, char ** argv)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
char opts[] = "+h";
|
|
Packit |
2d622a |
struct option long_opts[] = {
|
|
Packit |
2d622a |
{"help", no_argument, NULL, 'h'},
|
|
Packit |
2d622a |
{"disable", no_argument, NULL, MAP_BASE|MAP_DISABLE},
|
|
Packit |
2d622a |
{"text", no_argument, NULL, MAP_BASE|MAP_TEXT},
|
|
Packit |
2d622a |
{"data", no_argument, NULL, MAP_BASE|MAP_DATA},
|
|
Packit |
2d622a |
{0},
|
|
Packit |
2d622a |
};
|
|
Packit |
2d622a |
int ret = 0, index = 0, remap_opts = 0;
|
|
Packit |
2d622a |
int fd;
|
|
Packit |
2d622a |
const char *target;
|
|
Packit |
2d622a |
void *ehdr;
|
|
Packit |
2d622a |
unsigned long mapsize = getpagesize();
|
|
Packit |
2d622a |
int target_wordsize;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
while (ret != -1) {
|
|
Packit |
2d622a |
ret = getopt_long(argc, argv, opts, long_opts, &index);
|
|
Packit |
2d622a |
if (ret > 0 && (ret & MAP_BASE)) {
|
|
Packit |
2d622a |
remap_opts |= ret;
|
|
Packit |
2d622a |
continue;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
switch (ret) {
|
|
Packit |
2d622a |
case '?':
|
|
Packit |
2d622a |
print_usage();
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
case 'h':
|
|
Packit |
2d622a |
print_usage();
|
|
Packit |
2d622a |
exit(EXIT_SUCCESS);
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
default:
|
|
Packit |
2d622a |
ret = -1;
|
|
Packit |
2d622a |
break;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
index = optind;
|
|
Packit |
2d622a |
remap_opts &= ~MAP_BASE;
|
|
Packit |
2d622a |
if (remap_opts & MAP_DISABLE && remap_opts != MAP_DISABLE) {
|
|
Packit |
2d622a |
ERROR("--disable is not compatible with --text or --data\n");
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if ((argc - index) != 1) {
|
|
Packit |
2d622a |
print_usage();
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
target = argv[index];
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* We don't need write access unless we plan to alter the binary */
|
|
Packit |
2d622a |
fd = open(target, (remap_opts ? O_RDWR : O_RDONLY));
|
|
Packit |
2d622a |
if (fd < 0) {
|
|
Packit |
2d622a |
ERROR("Opening %s failed: %s\n", target, strerror(errno));
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
ehdr = mmap(NULL, mapsize, PROT_READ | (remap_opts ? PROT_WRITE : 0),
|
|
Packit |
2d622a |
MAP_SHARED, fd, 0);
|
|
Packit |
2d622a |
if (ehdr == MAP_FAILED) {
|
|
Packit |
2d622a |
ERROR("Mapping %s failed: %s\n", target, strerror(errno));
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
target_wordsize = check_elf_wordsize(ehdr);
|
|
Packit |
2d622a |
check_remap_elf(&ehdr, &mapsize, target_wordsize);
|
|
Packit |
2d622a |
if (target_wordsize == ELFCLASS64)
|
|
Packit |
2d622a |
update_phdrs64((Elf64_Ehdr *) ehdr, remap_opts);
|
|
Packit |
2d622a |
else
|
|
Packit |
2d622a |
update_phdrs32((Elf32_Ehdr *) ehdr, remap_opts);
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (munmap(ehdr, mapsize) != 0) {
|
|
Packit |
2d622a |
ERROR("Unmapping %s failed: %s\n", target, strerror(errno));
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
if (close(fd) != 0) {
|
|
Packit |
2d622a |
ERROR("Final close of %s failed: %s -- possible data loss!\n",
|
|
Packit |
2d622a |
target, strerror(errno));
|
|
Packit |
2d622a |
exit(EXIT_FAILURE);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
exit(EXIT_SUCCESS);
|
|
Packit |
2d622a |
}
|