1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "berberis/runtime_primitives/exec_region_elf_backed.h"
18 #include "berberis/tiny_loader/tiny_loader.h"
19
20 #include <android/dlext.h>
21 #include <dlfcn.h>
22 #include <sys/mman.h>
23
24 #include "berberis/base/bit_util.h"
25 #include "berberis/base/fd.h"
26 #include "berberis/base/mmap.h"
27
28 // Note that we have to use absolute path for ANDROID_DLEXT_FORCE_LOAD to work correctly
29 // otherwise searching by soname will trigger and the flag will have no effect.
30 #if defined(__LP64__)
31 const constexpr char* kExecRegionLibraryPath = "/system/lib64/libberberis_exec_region.so";
32 #else
33 const constexpr char* kExecRegionLibraryPath = "/system/lib/libberberis_exec_region.so";
34 #endif
35
36 const constexpr char* kRegionStartSymbolName = "exec_region_start";
37 const constexpr char* kRegionEndSymbolName = "exec_region_end";
38
39 namespace berberis {
40
Create(size_t size)41 ExecRegion ExecRegionElfBackedFactory::Create(size_t size) {
42 size = AlignUpPageSize(size);
43
44 // Since we cannot force android loader to map library in lower 2G memory we will need
45 // to reserve the space first and then direct the loader to load the library at that address.
46 size_t load_size = TinyLoader::CalculateLoadSize(kExecRegionLibraryPath, nullptr);
47 CHECK_NE(load_size, 0);
48
49 void* load_addr = MmapImplOrDie({.addr = nullptr,
50 .size = load_size,
51 .prot = PROT_NONE,
52 .flags = MAP_ANONYMOUS | MAP_PRIVATE,
53 .berberis_flags = kMmapBerberis32Bit});
54
55 android_dlextinfo dlextinfo{.flags = ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_RESERVED_ADDRESS,
56 .reserved_addr = load_addr,
57 .reserved_size = load_size};
58
59 void* handle = android_dlopen_ext(kExecRegionLibraryPath, RTLD_NOW, &dlextinfo);
60 if (handle == nullptr) {
61 FATAL("Couldn't load \"%s\": %s", kExecRegionLibraryPath, dlerror());
62 }
63 void* region_start = dlsym(handle, kRegionStartSymbolName);
64 CHECK(region_start != nullptr);
65 auto region_start_addr = bit_cast<uintptr_t>(region_start);
66 CHECK(region_start_addr % kPageSize == 0);
67
68 void* region_end = dlsym(handle, kRegionEndSymbolName);
69 CHECK(region_end != nullptr);
70 auto region_end_addr = bit_cast<uintptr_t>(region_end);
71 CHECK(region_end_addr % kPageSize == 0);
72 size_t region_size = region_end_addr - region_start_addr;
73 CHECK_GE(region_size, size);
74
75 auto fd = CreateMemfdOrDie("exec");
76 FtruncateOrDie(fd, static_cast<off64_t>(region_size));
77
78 ExecRegion result{
79 static_cast<uint8_t*>(MmapImplOrDie({.addr = region_start,
80 .size = region_size,
81 .prot = PROT_READ | PROT_EXEC,
82 .flags = MAP_FIXED | MAP_SHARED,
83 .fd = fd})),
84 static_cast<uint8_t*>(MmapImplOrDie(
85 {.size = region_size, .prot = PROT_READ | PROT_WRITE, .flags = MAP_SHARED, .fd = fd})),
86 region_size};
87
88 CloseUnsafe(fd);
89 return result;
90 }
91
92 } // namespace berberis
93