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