blob: e7447e4027728f281e73befa107841b7cf01f998 [file] [log] [blame]
Dmitriy Ivanova1feb112015-10-01 18:41:57 -07001/*
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 Ivanov48ec2882016-08-04 11:50:36 -070018
Dmitriy Ivanova1feb112015-10-01 18:41:57 -070019#include "linker_debug.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070020#include "linker_globals.h"
21
22#include "android-base/strings.h"
23
24#include <sys/stat.h>
25#include <unistd.h>
26
27std::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 Ivanova1feb112015-10-01 18:41:57 -070038
39bool normalize_path(const char* path, std::string* normalized_path) {
40 // Input should be an absolute path
41 if (path[0] != '/') {
Dimitry Ivanov769b33f2016-07-21 11:33:40 -070042 PRINT("normalize_path - invalid input: \"%s\", the input path should be absolute", path);
Dmitriy Ivanova1feb112015-10-01 18:41:57 -070043 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 Ivanov42d5fcb2015-10-29 17:01:24 -070083bool 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 Ivanov284ae352015-12-08 10:47:13 -080088 return strncmp(haystack, needle, needle_len) == 0 &&
89 haystack[needle_len] == '/' &&
90 strchr(haystack + needle_len + 1, '/') == nullptr;
91}
92
93bool 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 Ivanov42d5fcb2015-10-29 17:01:24 -0700100}
101
102const char* const kZipFileSeparator = "!/";
103
104bool 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 Ivanov769b33f2016-07-21 11:33:40 -0700111 TRACE("Trying zip file open from path \"%s\" -> normalized \"%s\"", input_path, path);
Dmitriy Ivanov42d5fcb2015-10-29 17:01:24 -0700112
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 Ivanov84bab5a2015-11-20 13:34:11 -0800136constexpr off64_t kPageMask = ~static_cast<off64_t>(PAGE_SIZE-1);
137
138off64_t page_start(off64_t offset) {
139 return offset & kPageMask;
140}
141
142bool 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
152size_t page_offset(off64_t offset) {
153 return static_cast<size_t>(offset & (PAGE_SIZE-1));
154}
155
Dimitry Ivanov48ec2882016-08-04 11:50:36 -0700156void 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
163void resolve_paths(std::vector<std::string>& paths,
164 std::vector<std::string>* resolved_paths) {
165 resolved_paths->clear();
166 for (const auto& path : paths) {
167 char resolved_path[PATH_MAX];
168 const char* original_path = path.c_str();
169 if (realpath(original_path, resolved_path) != nullptr) {
170 struct stat s;
171 if (stat(resolved_path, &s) == 0) {
172 if (S_ISDIR(s.st_mode)) {
173 resolved_paths->push_back(resolved_path);
174 } else {
175 DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path);
176 continue;
177 }
178 } else {
179 DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno));
180 continue;
181 }
182 } else {
183 std::string zip_path;
184 std::string entry_path;
185
186 std::string normalized_path;
187
188 if (!normalize_path(original_path, &normalized_path)) {
189 DL_WARN("Warning: unable to normalize \"%s\"", original_path);
190 continue;
191 }
192
193 if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) {
194 if (realpath(zip_path.c_str(), resolved_path) == nullptr) {
195 DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno));
196 continue;
197 }
198
199 resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path);
200 }
201 }
202 }
203}
204