| Dmitriy Ivanov | a1feb11 | 2015-10-01 18:41:57 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2015 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 "linker_utils.h" | 
| Dimitry Ivanov | 48ec288 | 2016-08-04 11:50:36 -0700 | [diff] [blame] | 18 |  | 
| Dmitriy Ivanov | a1feb11 | 2015-10-01 18:41:57 -0700 | [diff] [blame] | 19 | #include "linker_debug.h" | 
| Dimitry Ivanov | 48ec288 | 2016-08-04 11:50:36 -0700 | [diff] [blame] | 20 | #include "linker_globals.h" | 
|  | 21 |  | 
|  | 22 | #include "android-base/strings.h" | 
|  | 23 |  | 
|  | 24 | #include <sys/stat.h> | 
|  | 25 | #include <unistd.h> | 
|  | 26 |  | 
|  | 27 | std::string dirname(const char* path) { | 
|  | 28 | const char* last_slash = strrchr(path, '/'); | 
|  | 29 |  | 
|  | 30 | if (last_slash == path) { | 
|  | 31 | return "/"; | 
|  | 32 | } else if (last_slash == nullptr) { | 
|  | 33 | return "."; | 
|  | 34 | } else { | 
|  | 35 | return std::string(path, last_slash - path); | 
|  | 36 | } | 
|  | 37 | } | 
| Dmitriy Ivanov | a1feb11 | 2015-10-01 18:41:57 -0700 | [diff] [blame] | 38 |  | 
|  | 39 | bool normalize_path(const char* path, std::string* normalized_path) { | 
|  | 40 | // Input should be an absolute path | 
|  | 41 | if (path[0] != '/') { | 
| Dimitry Ivanov | 769b33f | 2016-07-21 11:33:40 -0700 | [diff] [blame] | 42 | PRINT("normalize_path - invalid input: \"%s\", the input path should be absolute", path); | 
| Dmitriy Ivanov | a1feb11 | 2015-10-01 18:41:57 -0700 | [diff] [blame] | 43 | return false; | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | const size_t len = strlen(path) + 1; | 
|  | 47 | char buf[len]; | 
|  | 48 |  | 
|  | 49 | const char* in_ptr = path; | 
|  | 50 | char* out_ptr = buf; | 
|  | 51 |  | 
|  | 52 | while (*in_ptr != 0) { | 
|  | 53 | if (*in_ptr == '/') { | 
|  | 54 | char c1 = in_ptr[1]; | 
|  | 55 | if (c1 == '.') { | 
|  | 56 | char c2 = in_ptr[2]; | 
|  | 57 | if (c2 == '/') { | 
|  | 58 | in_ptr += 2; | 
|  | 59 | continue; | 
|  | 60 | } else if (c2 == '.' && (in_ptr[3] == '/' || in_ptr[3] == 0)) { | 
|  | 61 | in_ptr += 3; | 
|  | 62 | while (out_ptr > buf && *--out_ptr != '/') { | 
|  | 63 | } | 
|  | 64 | if (in_ptr[0] == 0) { | 
|  | 65 | // retain '/' | 
|  | 66 | out_ptr++; | 
|  | 67 | } | 
|  | 68 | continue; | 
|  | 69 | } | 
|  | 70 | } else if (c1 == '/') { | 
|  | 71 | ++in_ptr; | 
|  | 72 | continue; | 
|  | 73 | } | 
|  | 74 | } | 
|  | 75 | *out_ptr++ = *in_ptr++; | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | *out_ptr = 0; | 
|  | 79 | *normalized_path = buf; | 
|  | 80 | return true; | 
|  | 81 | } | 
|  | 82 |  | 
| Dmitriy Ivanov | 42d5fcb | 2015-10-29 17:01:24 -0700 | [diff] [blame] | 83 | bool file_is_in_dir(const std::string& file, const std::string& dir) { | 
|  | 84 | const char* needle = dir.c_str(); | 
|  | 85 | const char* haystack = file.c_str(); | 
|  | 86 | size_t needle_len = strlen(needle); | 
|  | 87 |  | 
| Dimitry Ivanov | 284ae35 | 2015-12-08 10:47:13 -0800 | [diff] [blame] | 88 | return strncmp(haystack, needle, needle_len) == 0 && | 
|  | 89 | haystack[needle_len] == '/' && | 
|  | 90 | strchr(haystack + needle_len + 1, '/') == nullptr; | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | bool file_is_under_dir(const std::string& file, const std::string& dir) { | 
|  | 94 | const char* needle = dir.c_str(); | 
|  | 95 | const char* haystack = file.c_str(); | 
|  | 96 | size_t needle_len = strlen(needle); | 
|  | 97 |  | 
|  | 98 | return strncmp(haystack, needle, needle_len) == 0 && | 
|  | 99 | haystack[needle_len] == '/'; | 
| Dmitriy Ivanov | 42d5fcb | 2015-10-29 17:01:24 -0700 | [diff] [blame] | 100 | } | 
|  | 101 |  | 
|  | 102 | const char* const kZipFileSeparator = "!/"; | 
|  | 103 |  | 
|  | 104 | bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path) { | 
|  | 105 | std::string normalized_path; | 
|  | 106 | if (!normalize_path(input_path, &normalized_path)) { | 
|  | 107 | return false; | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | const char* const path = normalized_path.c_str(); | 
| Dimitry Ivanov | 769b33f | 2016-07-21 11:33:40 -0700 | [diff] [blame] | 111 | TRACE("Trying zip file open from path \"%s\" -> normalized \"%s\"", input_path, path); | 
| Dmitriy Ivanov | 42d5fcb | 2015-10-29 17:01:24 -0700 | [diff] [blame] | 112 |  | 
|  | 113 | // Treat an '!/' separator inside a path as the separator between the name | 
|  | 114 | // of the zip file on disk and the subdirectory to search within it. | 
|  | 115 | // For example, if path is "foo.zip!/bar/bas/x.so", then we search for | 
|  | 116 | // "bar/bas/x.so" within "foo.zip". | 
|  | 117 | const char* const separator = strstr(path, kZipFileSeparator); | 
|  | 118 | if (separator == nullptr) { | 
|  | 119 | return false; | 
|  | 120 | } | 
|  | 121 |  | 
|  | 122 | char buf[512]; | 
|  | 123 | if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) { | 
|  | 124 | PRINT("Warning: ignoring very long library path: %s", path); | 
|  | 125 | return false; | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | buf[separator - path] = '\0'; | 
|  | 129 |  | 
|  | 130 | *zip_path = buf; | 
|  | 131 | *entry_path = &buf[separator - path + 2]; | 
|  | 132 |  | 
|  | 133 | return true; | 
|  | 134 | } | 
|  | 135 |  | 
| Dmitriy Ivanov | 84bab5a | 2015-11-20 13:34:11 -0800 | [diff] [blame] | 136 | constexpr off64_t kPageMask = ~static_cast<off64_t>(PAGE_SIZE-1); | 
|  | 137 |  | 
|  | 138 | off64_t page_start(off64_t offset) { | 
|  | 139 | return offset & kPageMask; | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | bool safe_add(off64_t* out, off64_t a, size_t b) { | 
|  | 143 | CHECK(a >= 0); | 
|  | 144 | if (static_cast<uint64_t>(INT64_MAX - a) < b) { | 
|  | 145 | return false; | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | *out = a + b; | 
|  | 149 | return true; | 
|  | 150 | } | 
|  | 151 |  | 
|  | 152 | size_t page_offset(off64_t offset) { | 
|  | 153 | return static_cast<size_t>(offset & (PAGE_SIZE-1)); | 
|  | 154 | } | 
|  | 155 |  | 
| Dimitry Ivanov | 48ec288 | 2016-08-04 11:50:36 -0700 | [diff] [blame] | 156 | void split_path(const char* path, const char* delimiters, | 
|  | 157 | std::vector<std::string>* paths) { | 
|  | 158 | if (path != nullptr && path[0] != 0) { | 
|  | 159 | *paths = android::base::Split(path, delimiters); | 
|  | 160 | } | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | void resolve_paths(std::vector<std::string>& paths, | 
|  | 164 | std::vector<std::string>* resolved_paths) { | 
|  | 165 | resolved_paths->clear(); | 
|  | 166 | for (const auto& path : paths) { | 
| Dimitry Ivanov | 01fdb6a | 2016-09-07 14:48:27 -0700 | [diff] [blame] | 167 | // skip empty paths | 
|  | 168 | if (path.empty()) { | 
|  | 169 | continue; | 
|  | 170 | } | 
|  | 171 |  | 
| Dimitry Ivanov | 48ec288 | 2016-08-04 11:50:36 -0700 | [diff] [blame] | 172 | char resolved_path[PATH_MAX]; | 
|  | 173 | const char* original_path = path.c_str(); | 
|  | 174 | if (realpath(original_path, resolved_path) != nullptr) { | 
|  | 175 | struct stat s; | 
|  | 176 | if (stat(resolved_path, &s) == 0) { | 
|  | 177 | if (S_ISDIR(s.st_mode)) { | 
|  | 178 | resolved_paths->push_back(resolved_path); | 
|  | 179 | } else { | 
|  | 180 | DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path); | 
|  | 181 | continue; | 
|  | 182 | } | 
|  | 183 | } else { | 
|  | 184 | DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno)); | 
|  | 185 | continue; | 
|  | 186 | } | 
|  | 187 | } else { | 
|  | 188 | std::string zip_path; | 
|  | 189 | std::string entry_path; | 
|  | 190 |  | 
|  | 191 | std::string normalized_path; | 
|  | 192 |  | 
|  | 193 | if (!normalize_path(original_path, &normalized_path)) { | 
|  | 194 | DL_WARN("Warning: unable to normalize \"%s\"", original_path); | 
|  | 195 | continue; | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) { | 
|  | 199 | if (realpath(zip_path.c_str(), resolved_path) == nullptr) { | 
|  | 200 | DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno)); | 
|  | 201 | continue; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path); | 
|  | 205 | } | 
|  | 206 | } | 
|  | 207 | } | 
|  | 208 | } | 
|  | 209 |  |