Add file_offset parameter to android_extinfo
Bug: 17762003
(cherry picked from commit 07e5bc152d8a3ad4c50808bb86f3c0f2c5e2f514)
Change-Id: I72d527831384ff5dde013a4c8dfe639fbec165f5
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 9801fa1..3631d2f 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -232,7 +232,7 @@
static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
#endif
-static soinfo __libdl_info("libdl.so", nullptr);
+static soinfo __libdl_info("libdl.so", nullptr, 0);
// This is used by the dynamic linker. Every process gets these symbols for free.
soinfo* get_libdl_info() {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 1befaa6..d6d5f35 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -285,13 +285,13 @@
g_soinfo_links_allocator.protect_all(protection);
}
-static soinfo* soinfo_alloc(const char* name, struct stat* file_stat) {
+static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset) {
if (strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("library name \"%s\" too long", name);
return nullptr;
}
- soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat);
+ soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat, file_offset);
sonext->next = si;
sonext = si;
@@ -457,7 +457,7 @@
return nullptr;
}
-soinfo::soinfo(const char* name, const struct stat* file_stat) {
+soinfo::soinfo(const char* name, const struct stat* file_stat, off64_t file_offset) {
memset(this, 0, sizeof(*this));
strlcpy(this->name, name, sizeof(this->name));
@@ -465,8 +465,9 @@
version = SOINFO_VERSION;
if (file_stat != nullptr) {
- set_st_dev(file_stat->st_dev);
- set_st_ino(file_stat->st_ino);
+ this->st_dev = file_stat->st_dev;
+ this->st_ino = file_stat->st_ino;
+ this->file_offset = file_offset;
}
}
@@ -811,10 +812,14 @@
static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int dlflags, const android_dlextinfo* extinfo) {
int fd = -1;
+ off64_t file_offset = 0;
ScopedFd file_guard(-1);
if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
fd = extinfo->library_fd;
+ if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_OFFSET) != 0) {
+ file_offset = extinfo->library_offset;
+ }
} else {
// Open the file.
fd = open_library(name);
@@ -826,6 +831,11 @@
file_guard.reset(fd);
}
+ if ((file_offset % PAGE_SIZE) != 0) {
+ DL_ERR("file offset for the library %s is not page-aligned: %" PRId64, name, file_offset);
+ return nullptr;
+ }
+
struct stat file_stat;
if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno));
@@ -838,7 +848,8 @@
if (si->get_st_dev() != 0 &&
si->get_st_ino() != 0 &&
si->get_st_dev() == file_stat.st_dev &&
- si->get_st_ino() == file_stat.st_ino) {
+ si->get_st_ino() == file_stat.st_ino &&
+ si->get_file_offset() == file_offset) {
TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name);
return si;
}
@@ -850,12 +861,12 @@
}
// Read the ELF header and load the segments.
- ElfReader elf_reader(name, fd);
+ ElfReader elf_reader(name, fd, file_offset);
if (!elf_reader.Load(extinfo)) {
return nullptr;
}
- soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat);
+ soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat, file_offset);
if (si == nullptr) {
return nullptr;
}
@@ -1068,9 +1079,16 @@
DL_ERR("invalid flags to dlopen: %x", flags);
return nullptr;
}
- if (extinfo != nullptr && ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0)) {
- DL_ERR("invalid extended flags to android_dlopen_ext: %" PRIx64, extinfo->flags);
- return nullptr;
+ if (extinfo != nullptr) {
+ if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
+ DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
+ return nullptr;
+ }
+ if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 &&
+ (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_OFFSET) != 0) {
+ DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags);
+ return nullptr;
+ }
}
protect_data(PROT_READ | PROT_WRITE);
soinfo* si = find_library(name, flags, extinfo);
@@ -1752,18 +1770,6 @@
children.clear();
}
-void soinfo::set_st_dev(dev_t dev) {
- if (has_min_version(0)) {
- st_dev = dev;
- }
-}
-
-void soinfo::set_st_ino(ino_t ino) {
- if (has_min_version(0)) {
- st_ino = ino;
- }
-}
-
dev_t soinfo::get_st_dev() {
if (has_min_version(0)) {
return st_dev;
@@ -1780,6 +1786,14 @@
return 0;
}
+off64_t soinfo::get_file_offset() {
+ if (has_min_version(1)) {
+ return file_offset;
+ }
+
+ return 0;
+}
+
// This is a return on get_children()/get_parents() if
// 'this->flags' does not have FLAG_NEW_SOINFO set.
static soinfo::soinfo_list_t g_empty_list;
@@ -2200,7 +2214,7 @@
return;
}
- soinfo* si = soinfo_alloc("[vdso]", nullptr);
+ soinfo* si = soinfo_alloc("[vdso]", nullptr, 0);
si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
si->phnum = ehdr_vdso->e_phnum;
@@ -2221,7 +2235,7 @@
#else
#define LINKER_PATH "/system/bin/linker"
#endif
-static soinfo linker_soinfo_for_gdb(LINKER_PATH, nullptr);
+static soinfo linker_soinfo_for_gdb(LINKER_PATH, nullptr, 0);
/* gdb expects the linker to be in the debug shared object list.
* Without this, gdb has trouble locating the linker's ".text"
@@ -2285,7 +2299,7 @@
INFO("[ android linker & debugger ]");
- soinfo* si = soinfo_alloc(args.argv[0], nullptr);
+ soinfo* si = soinfo_alloc(args.argv[0], nullptr, 0);
if (si == nullptr) {
exit(EXIT_FAILURE);
}
@@ -2473,7 +2487,7 @@
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
- soinfo linker_so("[dynamic linker]", nullptr);
+ soinfo linker_so("[dynamic linker]", nullptr, 0);
// If the linker is not acting as PT_INTERP entry_point is equal to
// _start. Which means that the linker is running as an executable and
diff --git a/linker/linker.h b/linker/linker.h
index 3abab29..3b140ac 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -197,11 +197,9 @@
#if !defined(__LP64__)
bool has_text_relocations;
#endif
- // TODO: remove this flag, dynamic linker
- // should not use it in any way.
bool has_DT_SYMBOLIC;
- soinfo(const char* name, const struct stat* file_stat);
+ soinfo(const char* name, const struct stat* file_stat, off64_t file_offset);
void CallConstructors();
void CallDestructors();
@@ -212,10 +210,9 @@
void add_child(soinfo* child);
void remove_all_links();
- void set_st_dev(dev_t st_dev);
- void set_st_ino(ino_t st_ino);
ino_t get_st_ino();
dev_t get_st_dev();
+ off64_t get_file_offset();
soinfo_list_t& get_children();
soinfo_list_t& get_parents();
@@ -248,6 +245,7 @@
soinfo_list_t parents;
// version >= 1
+ off64_t file_offset;
};
extern soinfo* get_libdl_info();
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 44c8e9e..e0d6d0e 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -119,8 +119,8 @@
MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
-ElfReader::ElfReader(const char* name, int fd)
- : name_(name), fd_(fd),
+ElfReader::ElfReader(const char* name, int fd, off64_t file_offset)
+ : name_(name), fd_(fd), file_offset_(file_offset),
phdr_num_(0), phdr_mmap_(nullptr), phdr_table_(nullptr), phdr_size_(0),
load_start_(nullptr), load_size_(0), load_bias_(0),
loaded_phdr_(nullptr) {
@@ -142,6 +142,13 @@
}
bool ElfReader::ReadElfHeader() {
+ off64_t actual_offset = lseek64(fd_, file_offset_, SEEK_SET);
+
+ if (actual_offset != file_offset_) {
+ DL_ERR("seek to %" PRId64 " failed: %s", file_offset_, strerror(errno));
+ return false;
+ }
+
ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, &header_, sizeof(header_)));
if (rc < 0) {
DL_ERR("can't read file \"%s\": %s", name_, strerror(errno));
@@ -225,7 +232,7 @@
phdr_size_ = page_max - page_min;
- void* mmap_result = mmap(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, page_min);
+ void* mmap_result = mmap64(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, file_offset_ + page_min);
if (mmap_result == MAP_FAILED) {
DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno));
return false;
@@ -356,12 +363,12 @@
ElfW(Addr) file_length = file_end - file_page_start;
if (file_length != 0) {
- void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start),
+ void* seg_addr = mmap64(reinterpret_cast<void*>(seg_page_start),
file_length,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_PRIVATE,
fd_,
- file_page_start);
+ file_offset_ + file_page_start);
if (seg_addr == MAP_FAILED) {
DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno));
return false;
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 593fb5a..65d302c 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -39,7 +39,7 @@
class ElfReader {
public:
- ElfReader(const char* name, int fd);
+ ElfReader(const char* name, int fd, off64_t file_offset);
~ElfReader();
bool Load(const android_dlextinfo* extinfo);
@@ -61,6 +61,7 @@
const char* name_;
int fd_;
+ off64_t file_offset_;
ElfW(Ehdr) header_;
size_t phdr_num_;