Blame hugeedit.c

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
}