12f09f445SMaksim Panchenko //===- bolt/runtime/hugify.cpp --------------------------------------------===//
29bd71615SXun Li //
3da752c9cSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4da752c9cSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5da752c9cSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69bd71615SXun Li //
79bd71615SXun Li //===----------------------------------------------------------------------===//
89bd71615SXun Li
9cb8d701bSVladislav Khmelevsky #if defined (__x86_64__)
10bbd9d610SAlexander Shaposhnikov #if !defined(__APPLE__)
11bbd9d610SAlexander Shaposhnikov
129bd71615SXun Li #include "common.h"
139bd71615SXun Li #include <sys/mman.h>
149bd71615SXun Li
159bd71615SXun Li // Enables a very verbose logging to stderr useful when debugging
169bd71615SXun Li //#define ENABLE_DEBUG
179bd71615SXun Li
189bd71615SXun Li // Function pointers to init routines in the binary, so we can resume
199bd71615SXun Li // regular execution of the function that we hooked.
209bd71615SXun Li extern void (*__bolt_hugify_init_ptr)();
219bd71615SXun Li
229bd71615SXun Li // The __hot_start and __hot_end symbols set by Bolt. We use them to figure
239bd71615SXun Li // out the rage for marking huge pages.
249bd71615SXun Li extern uint64_t __hot_start;
259bd71615SXun Li extern uint64_t __hot_end;
269bd71615SXun Li
279bd71615SXun Li #ifdef MADV_HUGEPAGE
282da5b12aSAmir Ayupov /// Check whether the kernel supports THP via corresponding sysfs entry.
has_pagecache_thp_support()2984eae1a4SXun Li static bool has_pagecache_thp_support() {
302da5b12aSAmir Ayupov char buf[256] = {0};
312da5b12aSAmir Ayupov const char *madviseStr = "always [madvise] never";
329bd71615SXun Li
332da5b12aSAmir Ayupov int fd = __open("/sys/kernel/mm/transparent_hugepage/enabled",
342da5b12aSAmir Ayupov 0 /* O_RDONLY */, 0);
352da5b12aSAmir Ayupov if (fd < 0)
369bd71615SXun Li return false;
372da5b12aSAmir Ayupov
382da5b12aSAmir Ayupov size_t res = __read(fd, buf, 256);
392da5b12aSAmir Ayupov if (res < 0)
409bd71615SXun Li return false;
412da5b12aSAmir Ayupov
422da5b12aSAmir Ayupov int cmp = strnCmp(buf, madviseStr, strLen(madviseStr));
432da5b12aSAmir Ayupov return cmp == 0;
449bd71615SXun Li }
459bd71615SXun Li
hugify_for_old_kernel(uint8_t * from,uint8_t * to)4684eae1a4SXun Li static void hugify_for_old_kernel(uint8_t *from, uint8_t *to) {
479bd71615SXun Li size_t size = to - from;
489bd71615SXun Li
499bd71615SXun Li uint8_t *mem = reinterpret_cast<uint8_t *>(
509bd71615SXun Li __mmap(0, size, 0x3 /* PROT_READ | PROT_WRITE*/,
519bd71615SXun Li 0x22 /* MAP_PRIVATE | MAP_ANONYMOUS*/, -1, 0));
529bd71615SXun Li
539bd71615SXun Li if (mem == (void *)MAP_FAILED) {
549bd71615SXun Li char msg[] = "Could not allocate memory for text move\n";
559bd71615SXun Li reportError(msg, sizeof(msg));
569bd71615SXun Li }
579bd71615SXun Li #ifdef ENABLE_DEBUG
589bd71615SXun Li reportNumber("Allocated temporary space: ", (uint64_t)mem, 16);
599bd71615SXun Li #endif
609bd71615SXun Li
619bd71615SXun Li // Copy the hot code to a temproary location.
62*ea2182feSMaksim Panchenko memcpy(mem, from, size);
639bd71615SXun Li
649bd71615SXun Li // Maps out the existing hot code.
659bd71615SXun Li if (__mmap(reinterpret_cast<uint64_t>(from), size,
669bd71615SXun Li PROT_READ | PROT_WRITE | PROT_EXEC,
679bd71615SXun Li MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
689bd71615SXun Li 0) == (void *)MAP_FAILED) {
699bd71615SXun Li char msg[] = "failed to mmap memory for large page move terminating\n";
709bd71615SXun Li reportError(msg, sizeof(msg));
719bd71615SXun Li }
729bd71615SXun Li
739bd71615SXun Li // Mark the hot code page to be huge page.
749bd71615SXun Li if (__madvise(from, size, MADV_HUGEPAGE) == -1) {
759bd71615SXun Li char msg[] = "failed to allocate large page\n";
769bd71615SXun Li reportError(msg, sizeof(msg));
779bd71615SXun Li }
789bd71615SXun Li
799bd71615SXun Li // Copy the hot code back.
80*ea2182feSMaksim Panchenko memcpy(from, mem, size);
819bd71615SXun Li
829bd71615SXun Li // Change permission back to read-only, ignore failure
839bd71615SXun Li __mprotect(from, size, PROT_READ | PROT_EXEC);
849bd71615SXun Li
859bd71615SXun Li __munmap(mem, size);
869bd71615SXun Li }
879bd71615SXun Li #endif
889bd71615SXun Li
__bolt_hugify_self_impl()899bd71615SXun Li extern "C" void __bolt_hugify_self_impl() {
909bd71615SXun Li #ifdef MADV_HUGEPAGE
919bd71615SXun Li uint8_t *hotStart = (uint8_t *)&__hot_start;
929bd71615SXun Li uint8_t *hotEnd = (uint8_t *)&__hot_end;
939bd71615SXun Li // Make sure the start and end are aligned with huge page address
949bd71615SXun Li const size_t hugePageBytes = 2L * 1024 * 1024;
959bd71615SXun Li uint8_t *from = hotStart - ((intptr_t)hotStart & (hugePageBytes - 1));
969bd71615SXun Li uint8_t *to = hotEnd + (hugePageBytes - 1);
979bd71615SXun Li to -= (intptr_t)to & (hugePageBytes - 1);
989bd71615SXun Li
999bd71615SXun Li #ifdef ENABLE_DEBUG
1009bd71615SXun Li reportNumber("[hugify] hot start: ", (uint64_t)hotStart, 16);
1019bd71615SXun Li reportNumber("[hugify] hot end: ", (uint64_t)hotEnd, 16);
1029bd71615SXun Li reportNumber("[hugify] aligned huge page from: ", (uint64_t)from, 16);
1039bd71615SXun Li reportNumber("[hugify] aligned huge page to: ", (uint64_t)to, 16);
1049bd71615SXun Li #endif
1059bd71615SXun Li
1069bd71615SXun Li if (!has_pagecache_thp_support()) {
1079bd71615SXun Li hugify_for_old_kernel(from, to);
1089bd71615SXun Li return;
1099bd71615SXun Li }
1109bd71615SXun Li
1119bd71615SXun Li if (__madvise(from, (to - from), MADV_HUGEPAGE) == -1) {
1129bd71615SXun Li char msg[] = "failed to allocate large page\n";
1139bd71615SXun Li // TODO: allow user to control the failure behavior.
1149bd71615SXun Li reportError(msg, sizeof(msg));
1159bd71615SXun Li }
1169bd71615SXun Li #endif
1179bd71615SXun Li }
1189bd71615SXun Li
1199bd71615SXun Li /// This is hooking ELF's entry, it needs to save all machine state.
__bolt_hugify_self()1209bd71615SXun Li extern "C" __attribute((naked)) void __bolt_hugify_self() {
121c6799a68SRafael Auler __asm__ __volatile__(SAVE_ALL
122c6799a68SRafael Auler "call __bolt_hugify_self_impl\n"
123c6799a68SRafael Auler RESTORE_ALL
124c6799a68SRafael Auler "jmp *__bolt_hugify_init_ptr(%%rip)\n"
125c6799a68SRafael Auler :::);
1269bd71615SXun Li }
127bbd9d610SAlexander Shaposhnikov
128bbd9d610SAlexander Shaposhnikov #endif
129cb8d701bSVladislav Khmelevsky #endif
130