Project import
diff --git a/linker/Android.bp b/linker/Android.bp new file mode 100644 index 0000000..39f1da9 --- /dev/null +++ b/linker/Android.bp
@@ -0,0 +1,148 @@ +cc_library_static { + name: "liblinker_malloc", + clang: true, + + srcs: [ + "linker_allocator.cpp", + "linker_memory.cpp", + ], + + // We need to access Bionic private headers in the linker. + include_dirs: ["bionic/libc"], +} + +cc_binary { + clang: true, + + srcs: [ + "dlfcn.cpp", + "linker.cpp", + "linker_block_allocator.cpp", + "linker_dlwarning.cpp", + "linker_gdb_support.cpp", + "linker_globals.cpp", + "linker_libc_support.c", + "linker_namespaces.cpp", + "linker_logger.cpp", + "linker_mapped_file_fragment.cpp", + "linker_phdr.cpp", + "linker_sdk_versions.cpp", + "linker_soinfo.cpp", + "linker_utils.cpp", + "rt.cpp", + ], + + arch: { + arm: { + srcs: ["arch/arm/begin.S"], + + cflags: ["-D__work_around_b_24465209__"], + }, + arm64: { + srcs: ["arch/arm64/begin.S"], + }, + x86: { + srcs: ["arch/x86/begin.c"], + + cflags: ["-D__work_around_b_24465209__"], + }, + x86_64: { + srcs: ["arch/x86_64/begin.S"], + }, + mips: { + srcs: [ + "arch/mips/begin.S", + "linker_mips.cpp", + ], + }, + mips64: { + srcs: [ + "arch/mips64/begin.S", + "linker_mips.cpp", + ], + }, + }, + + // We need to access Bionic private headers in the linker. + include_dirs: ["bionic/libc"], + + // -shared is used to overwrite the -Bstatic and -static + // flags triggered by LOCAL_FORCE_STATIC_EXECUTABLE. + // This dynamic linker is actually a shared object linked with static libraries. + ldflags: [ + "-shared", + "-Wl,-Bsymbolic", + "-Wl,--exclude-libs,ALL", + ], + + cflags: [ + "-fno-stack-protector", + "-Wstrict-overflow=5", + "-fvisibility=hidden", + "-Wall", + "-Wextra", + "-Wunused", + "-Werror", + ], + + // TODO: split out the asflags. + asflags: [ + "-fno-stack-protector", + "-Wstrict-overflow=5", + "-fvisibility=hidden", + "-Wall", + "-Wextra", + "-Wunused", + "-Werror", + ], + + conlyflags: ["-std=gnu99"], + + cppflags: ["-Wold-style-cast"], + + // we don't want crtbegin.o (because we have begin.o), so unset it + // just for this module + nocrt: true, + + static_libs: [ + "libc_nomalloc", + "libziparchive", + "libutils", + "libbase", + "libz", + "liblog", + "libdebuggerd_client", + + // Important: The liblinker_malloc should be the last library in the list + // to overwrite any other malloc implementations by other static libraries. + "liblinker_malloc" + ], + static_executable: true, + + name: "linker", + multilib: { + lib32: { + symlinks: ["linker_asan"], + }, + lib64: { + suffix: "64", + symlinks: ["linker_asan64"], + }, + }, + target: { + android64: { + cflags: ["-DTARGET_IS_64_BIT"], + }, + }, + compile_multilib: "both", + + // Leave the symbols in the shared library so that stack unwinders can produce + // meaningful name resolution. + strip: { + keep_symbols: true, + }, + + // Insert an extra objcopy step to add prefix to symbols. This is needed to prevent gdb + // looking up symbols in the linker by mistake. + prefix_symbols: "__dl_", +}
diff --git a/linker/Android.mk b/linker/Android.mk new file mode 100644 index 0000000..ea7451c --- /dev/null +++ b/linker/Android.mk
@@ -0,0 +1,3 @@ +LOCAL_PATH := $(call my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/linker/MODULE_LICENSE_APACHE2 b/linker/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/linker/MODULE_LICENSE_APACHE2
diff --git a/linker/NOTICE b/linker/NOTICE new file mode 100644 index 0000000..3ba0ff9 --- /dev/null +++ b/linker/NOTICE
@@ -0,0 +1,220 @@ +Copyright (C) 2007 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------- + +Copyright (C) 2008 The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (C) 2012 The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (C) 2013 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------- + +Copyright (C) 2013 The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (C) 2014 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------- + +Copyright (C) 2015 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------- + +Copyright (C) 2015 The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (C) 2016 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------- + +Copyright (C) 2016 The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- +
diff --git a/linker/arch/arm/begin.S b/linker/arch/arm/begin.S new file mode 100644 index 0000000..8cb599b --- /dev/null +++ b/linker/arch/arm/begin.S
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <private/bionic_asm.h> + +ENTRY(_start) + mov r0, sp + bl __linker_init + + /* linker init returns the _entry address in the main image */ + mov pc, r0 +END(_start)
diff --git a/linker/arch/arm64/begin.S b/linker/arch/arm64/begin.S new file mode 100644 index 0000000..a6ea583 --- /dev/null +++ b/linker/arch/arm64/begin.S
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <private/bionic_asm.h> + +ENTRY(_start) + mov x0, sp + bl __linker_init + + /* linker init returns the _entry address in the main image */ + br x0 +END(_start)
diff --git a/linker/arch/mips/begin.S b/linker/arch/mips/begin.S new file mode 100644 index 0000000..cbe1e37 --- /dev/null +++ b/linker/arch/mips/begin.S
@@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .align 4 + .type __start,@function + + .ent __start + .globl __start + .globl _start +__start: +_start: + .set noreorder + bal 1f + nop +1: .cpload $31 + .set reorder + + /* Discover the load address */ + la $t0, 1f + bal 1f +1: subu $t0, $ra, $t0 + +#define DT_PLTGOT 3 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a + + /* Search dynamic table for DT_MIPS_LOCAL_GOTNO and DT_PLTGOT values */ + la $t1, _DYNAMIC + addu $t1, $t0 + li $t3, DT_PLTGOT + li $t4, DT_MIPS_LOCAL_GOTNO +0: + lw $t2, 0($t1) + beqz $t2, .Lrelocate_local_got + + bne $t2, $t3, 1f /* DT_PLTGOT? */ + lw $s0, 4($t1) + addu $s0, $t0 + b 2f + +1: bne $t2, $t4, 1f /* DT_MIPS_LOCAL_GOTNO? */ + lw $s1, 4($t1) + b 2f + +1: +2: addu $t1, 8 + b 0b + +.Lrelocate_local_got: + /* + * Relocate the local GOT entries + * got[0] is address of lazy resolver function + * got[1] may be used for a GNU extension + */ + + addu $s0, 4 + subu $s1, 1 + lw $t1, ($s0) + bgez $t1, 9f + addu $s0, 4 + subu $s1, 1 + b 9f + +1: lw $t1, ($s0) + addu $t1, $t0 + sw $t1, ($s0) + addu $s0, 4 +9: subu $s1, 1 + bgez $s1, 1b + + /* call linker_init */ + move $a0, $sp + addiu $sp, -4*4 /* space for arg saves in linker_init */ + la $t9, __linker_init + jalr $t9 + move $t9, $v0 + addu $sp, 4*4 /* restore sp */ + j $t9 + .end __start
diff --git a/linker/arch/mips64/begin.S b/linker/arch/mips64/begin.S new file mode 100644 index 0000000..8f6b94a --- /dev/null +++ b/linker/arch/mips64/begin.S
@@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <private/bionic_asm.h> + +#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32) +#define ELF_DYNSZ 8 +#define ELF_DYN_TAG 0 +#define ELF_DYN_VAL 4 +#define GOTENT_SZ 4 +#else +#define ELF_DYNSZ 16 +#define ELF_DYN_TAG 0 +#define ELF_DYN_VAL 8 +#define GOTENT_SZ 8 +#endif + + .text + .align 4 + .type __start,@function + + .ent __start + .globl __start + .globl _start +__start: +_start: + .set noreorder + bal 1f + nop +1: +#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32) + .cpload ra +#else + .cpsetup ra, $0, 1b +#endif + .set reorder + + /* Discover the load address */ + LA t0, 1b + PTR_SUBU t0, ra, t0 + +#define DT_PLTGOT 3 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a + + /* Search dynamic table for DT_MIPS_LOCAL_GOTNO and DT_PLTGOT values */ + LA t1, _DYNAMIC + PTR_ADDU t1, t0 + LI t3, DT_PLTGOT + LI ta0, DT_MIPS_LOCAL_GOTNO +0: + REG_L t2, ELF_DYN_TAG(t1) + beqz t2, .Lrelocate_local_got + + bne t2, t3, 1f /* DT_PLTGOT? */ + REG_L s0, ELF_DYN_VAL(t1) + PTR_ADDU s0, t0 + b 2f + +1: + bne t2, ta0, 2f /* DT_MIPS_LOCAL_GOTNO? */ + REG_L s1, ELF_DYN_VAL(t1) + +2: PTR_ADDU t1, ELF_DYNSZ + b 0b + +.Lrelocate_local_got: + /* + * Relocate the local GOT entries + * got[0] is address of lazy resolver function + * got[1] may be used for a GNU extension + */ + + PTR_ADDU s0, GOTENT_SZ + SUBU s1, 1 + PTR_L t1, (s0) + bgez t1, 9f + PTR_ADDU s0, GOTENT_SZ + SUBU s1, 1 + b 9f + +1: PTR_L t1, (s0) + PTR_ADDU t1, t0 + PTR_S t1, (s0) + PTR_ADDU s0, GOTENT_SZ +9: SUBU s1, 1 + bgez s1, 1b + + /* call linker_init */ + move a0, sp +#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32) + PTR_SUBU sp, 4*REGSZ /* space for arg saves in linker_init */ +#endif + jal __linker_init +#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32) + PTR_ADDU sp, 4*REGSZ /* restore sp */ +#endif + move t9, v0 + j t9 + .end __start
diff --git a/linker/arch/x86/begin.c b/linker/arch/x86/begin.c new file mode 100644 index 0000000..051a16a --- /dev/null +++ b/linker/arch/x86/begin.c
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdint.h> +#include <sys/cdefs.h> + +extern unsigned __linker_init(void* raw_args); + +__LIBC_HIDDEN__ void _start() { + void (*start)(void); + + void* raw_args = (void*) ((uintptr_t) __builtin_frame_address(0) + sizeof(void*)); + start = (void(*)(void))__linker_init(raw_args); + + /* linker init returns (%eax) the _entry address in the main image */ + /* entry point expects sp to point to raw_args */ + + __asm__ ( + "mov %0, %%esp\n\t" + "jmp *%1\n\t" + : : "r"(raw_args), "r"(start) : + ); + + /* Unreachable */ +} + +/* Since linker has its own version of crtbegin (this file) it should have */ +/* own version of __stack_chk_fail_local for the case when it's built with */ +/* stack protector feature */ + +#include "arch-x86/bionic/__stack_chk_fail_local.h"
diff --git a/linker/arch/x86_64/begin.S b/linker/arch/x86_64/begin.S new file mode 100644 index 0000000..7945a31 --- /dev/null +++ b/linker/arch/x86_64/begin.S
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <private/bionic_asm.h> + +ENTRY(_start) + mov %rsp, %rdi + call __linker_init + + /* linker init returns (%rax) the _entry address in the main image */ + jmp *%rax +END(_start)
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp new file mode 100644 index 0000000..3ac61d7 --- /dev/null +++ b/linker/dlfcn.cpp
@@ -0,0 +1,288 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker.h" +#include "linker_globals.h" +#include "linker_dlwarning.h" + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <android/api-level.h> + +#include <bionic/pthread_internal.h> +#include "private/bionic_tls.h" +#include "private/ScopedPthreadMutexLocker.h" +#include "private/ThreadLocalBuffer.h" + +/* This file hijacks the symbols stubbed out in libdl.so. */ + +static pthread_mutex_t g_dl_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +static char* __bionic_set_dlerror(char* new_value) { + char** dlerror_slot = &reinterpret_cast<char**>(__get_tls())[TLS_SLOT_DLERROR]; + + char* old_value = *dlerror_slot; + *dlerror_slot = new_value; + return old_value; +} + +static void __bionic_format_dlerror(const char* msg, const char* detail) { + char* buffer = __get_thread()->dlerror_buffer; + strlcpy(buffer, msg, __BIONIC_DLERROR_BUFFER_SIZE); + if (detail != nullptr) { + strlcat(buffer, ": ", __BIONIC_DLERROR_BUFFER_SIZE); + strlcat(buffer, detail, __BIONIC_DLERROR_BUFFER_SIZE); + } + + __bionic_set_dlerror(buffer); +} + +char* dlerror() { + char* old_value = __bionic_set_dlerror(nullptr); + return old_value; +} + +void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + do_android_get_LD_LIBRARY_PATH(buffer, buffer_size); +} + +void android_update_LD_LIBRARY_PATH(const char* ld_library_path) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + do_android_update_LD_LIBRARY_PATH(ld_library_path); +} + +static void* dlopen_ext(const char* filename, int flags, + const android_dlextinfo* extinfo, void* caller_addr) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + g_linker_logger.ResetState(); + void* result = do_dlopen(filename, flags, extinfo, caller_addr); + if (result == nullptr) { + __bionic_format_dlerror("dlopen failed", linker_get_error_buffer()); + return nullptr; + } + return result; +} + +void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* extinfo) { + void* caller_addr = __builtin_return_address(0); + return dlopen_ext(filename, flags, extinfo, caller_addr); +} + +void* dlopen(const char* filename, int flags) { + void* caller_addr = __builtin_return_address(0); + return dlopen_ext(filename, flags, nullptr, caller_addr); +} + +void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + g_linker_logger.ResetState(); + void* result; + if (!do_dlsym(handle, symbol, version, caller_addr, &result)) { + __bionic_format_dlerror(linker_get_error_buffer(), nullptr); + return nullptr; + } + + return result; +} + +void* dlsym(void* handle, const char* symbol) { + void* caller_addr = __builtin_return_address(0); + return dlsym_impl(handle, symbol, nullptr, caller_addr); +} + +void* dlvsym(void* handle, const char* symbol, const char* version) { + void* caller_addr = __builtin_return_address(0); + return dlsym_impl(handle, symbol, version, caller_addr); +} + +int dladdr(const void* addr, Dl_info* info) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + return do_dladdr(addr, info); +} + +int dlclose(void* handle) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + int result = do_dlclose(handle); + if (result != 0) { + __bionic_format_dlerror("dlclose failed", linker_get_error_buffer()); + } + return result; +} + +int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + return do_dl_iterate_phdr(cb, data); +} + +void android_set_application_target_sdk_version(uint32_t target) { + // lock to avoid modification in the middle of dlopen. + ScopedPthreadMutexLocker locker(&g_dl_mutex); + set_application_target_sdk_version(target); +} + +uint32_t android_get_application_target_sdk_version() { + return get_application_target_sdk_version(); +} + +void android_dlwarning(void* obj, void (*f)(void*, const char*)) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + get_dlwarning(obj, f); +} + +bool android_init_namespaces(const char* public_ns_sonames, + const char* anon_ns_library_path) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + bool success = init_namespaces(public_ns_sonames, anon_ns_library_path); + if (!success) { + __bionic_format_dlerror("android_init_namespaces failed", linker_get_error_buffer()); + } + + return success; +} + +android_namespace_t* android_create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent_namespace) { + void* caller_addr = __builtin_return_address(0); + ScopedPthreadMutexLocker locker(&g_dl_mutex); + + android_namespace_t* result = create_namespace(caller_addr, + name, + ld_library_path, + default_library_path, + type, + permitted_when_isolated_path, + parent_namespace); + + if (result == nullptr) { + __bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer()); + } + + return result; +} + +// name_offset: starting index of the name in libdl_info.strtab +#define ELF32_SYM_INITIALIZER(name_offset, value, shndx) \ + { name_offset, \ + reinterpret_cast<Elf32_Addr>(value), \ + /* st_size */ 0, \ + ((shndx) == 0) ? 0 : (STB_GLOBAL << 4), \ + /* st_other */ 0, \ + shndx, \ + } + +#define ELF64_SYM_INITIALIZER(name_offset, value, shndx) \ + { name_offset, \ + ((shndx) == 0) ? 0 : (STB_GLOBAL << 4), \ + /* st_other */ 0, \ + shndx, \ + reinterpret_cast<Elf64_Addr>(value), \ + /* st_size */ 0, \ + } + +static const char ANDROID_LIBDL_STRTAB[] = + // 0000000 00011111 111112 22222222 2333333 3333444444444455555555556666666 6667777777777888888888899999 99999 + // 0123456 78901234 567890 12345678 9012345 6789012345678901234567890123456 7890123456789012345678901234 56789 + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0android_update_LD_LIBRARY_PATH\0android_get_LD_LIBRARY_PATH\0dl_it" + // 00000000001 1111111112222222222 3333333333444444444455555555556666666666777 777777788888888889999999999 + // 01234567890 1234567890123456789 0123456789012345678901234567890123456789012 345678901234567890123456789 + "erate_phdr\0android_dlopen_ext\0android_set_application_target_sdk_version\0android_get_application_tar" + // 0000000000111111 111122222222223333333333 4444444444555555555566666 6666677 777777778888888888 + // 0123456789012345 678901234567890123456789 0123456789012345678901234 5678901 234567890123456789 + "get_sdk_version\0android_init_namespaces\0android_create_namespace\0dlvsym\0android_dlwarning\0" +#if defined(__arm__) + // 290 + "dl_unwind_find_exidx\0" +#endif + ; + +static ElfW(Sym) g_libdl_symtab[] = { + // Total length of libdl_info.strtab, including trailing 0. + // This is actually the STH_UNDEF entry. Technically, it's + // supposed to have st_name == 0, but instead, it points to an index + // in the strtab with a \0 to make iterating through the symtab easier. + ELFW(SYM_INITIALIZER)(sizeof(ANDROID_LIBDL_STRTAB) - 1, nullptr, 0), + ELFW(SYM_INITIALIZER)( 0, &dlopen, 1), + ELFW(SYM_INITIALIZER)( 7, &dlclose, 1), + ELFW(SYM_INITIALIZER)( 15, &dlsym, 1), + ELFW(SYM_INITIALIZER)( 21, &dlerror, 1), + ELFW(SYM_INITIALIZER)( 29, &dladdr, 1), + ELFW(SYM_INITIALIZER)( 36, &android_update_LD_LIBRARY_PATH, 1), + ELFW(SYM_INITIALIZER)( 67, &android_get_LD_LIBRARY_PATH, 1), + ELFW(SYM_INITIALIZER)( 95, &dl_iterate_phdr, 1), + ELFW(SYM_INITIALIZER)(111, &android_dlopen_ext, 1), + ELFW(SYM_INITIALIZER)(130, &android_set_application_target_sdk_version, 1), + ELFW(SYM_INITIALIZER)(173, &android_get_application_target_sdk_version, 1), + ELFW(SYM_INITIALIZER)(216, &android_init_namespaces, 1), + ELFW(SYM_INITIALIZER)(240, &android_create_namespace, 1), + ELFW(SYM_INITIALIZER)(265, &dlvsym, 1), + ELFW(SYM_INITIALIZER)(272, &android_dlwarning, 1), +#if defined(__arm__) + ELFW(SYM_INITIALIZER)(290, &dl_unwind_find_exidx, 1), +#endif +}; + +// Fake out a hash table with a single bucket. +// +// A search of the hash table will look through g_libdl_symtab starting with index 1, then +// use g_libdl_chains to find the next index to look at. g_libdl_chains should be set up to +// walk through every element in g_libdl_symtab, and then end with 0 (sentinel value). +// +// That is, g_libdl_chains should look like { 0, 2, 3, ... N, 0 } where N is the number +// of actual symbols, or nelems(g_libdl_symtab)-1 (since the first element of g_libdl_symtab is not +// a real symbol). (See soinfo_elf_lookup().) +// +// Note that adding any new symbols here requires stubbing them out in libdl. +static unsigned g_libdl_buckets[1] = { 1 }; +#if defined(__arm__) +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 }; +#else +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }; +#endif + +static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); +static soinfo* __libdl_info = nullptr; + +// This is used by the dynamic linker. Every process gets these symbols for free. +soinfo* get_libdl_info() { + if (__libdl_info == nullptr) { + __libdl_info = new (__libdl_info_buf) soinfo(&g_default_namespace, "libdl.so", nullptr, 0, RTLD_GLOBAL); + __libdl_info->flags_ |= FLAG_LINKED; + __libdl_info->strtab_ = ANDROID_LIBDL_STRTAB; + __libdl_info->symtab_ = g_libdl_symtab; + __libdl_info->nbucket_ = sizeof(g_libdl_buckets)/sizeof(unsigned); + __libdl_info->nchain_ = sizeof(g_libdl_chains)/sizeof(unsigned); + __libdl_info->bucket_ = g_libdl_buckets; + __libdl_info->chain_ = g_libdl_chains; + __libdl_info->ref_count_ = 1; + __libdl_info->strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB); + __libdl_info->local_group_root_ = __libdl_info; + __libdl_info->soname_ = "libdl.so"; + __libdl_info->target_sdk_version_ = __ANDROID_API__; + __libdl_info->generate_handle(); +#if defined(__work_around_b_24465209__) + strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_)); +#endif + } + + return __libdl_info; +}
diff --git a/linker/linked_list.h b/linker/linked_list.h new file mode 100644 index 0000000..092e831 --- /dev/null +++ b/linker/linked_list.h
@@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKED_LIST_H +#define __LINKED_LIST_H + +#include "private/bionic_macros.h" + +template<typename T> +struct LinkedListEntry { + LinkedListEntry<T>* next; + T* element; +}; + +// ForwardInputIterator +template<typename T> +class LinkedListIterator { + public: + LinkedListIterator() : entry_(nullptr) {} + LinkedListIterator(const LinkedListIterator<T>& that) : entry_(that.entry_) {} + explicit LinkedListIterator(LinkedListEntry<T>* entry) : entry_(entry) {} + + LinkedListIterator<T>& operator=(const LinkedListIterator<T>& that) { + entry_ = that.entry_; + return *this; + } + + LinkedListIterator<T>& operator++() { + entry_ = entry_->next; + return *this; + } + + T* const operator*() { + return entry_->element; + } + + bool operator==(const LinkedListIterator<T>& that) const { + return entry_ == that.entry_; + } + + bool operator!=(const LinkedListIterator<T>& that) const { + return entry_ != that.entry_; + } + + private: + LinkedListEntry<T> *entry_; +}; + +/* + * Represents linked list of objects of type T + */ +template<typename T, typename Allocator> +class LinkedList { + public: + typedef LinkedListIterator<T> iterator; + typedef T* value_type; + + LinkedList() : head_(nullptr), tail_(nullptr) {} + ~LinkedList() { + clear(); + } + + LinkedList(LinkedList&& that) { + this->head_ = that.head_; + this->tail_ = that.tail_; + that.head_ = that.tail_ = nullptr; + } + + void push_front(T* const element) { + LinkedListEntry<T>* new_entry = Allocator::alloc(); + new_entry->next = head_; + new_entry->element = element; + head_ = new_entry; + if (tail_ == nullptr) { + tail_ = new_entry; + } + } + + void push_back(T* const element) { + LinkedListEntry<T>* new_entry = Allocator::alloc(); + new_entry->next = nullptr; + new_entry->element = element; + if (tail_ == nullptr) { + tail_ = head_ = new_entry; + } else { + tail_->next = new_entry; + tail_ = new_entry; + } + } + + T* pop_front() { + if (head_ == nullptr) { + return nullptr; + } + + LinkedListEntry<T>* entry = head_; + T* element = entry->element; + head_ = entry->next; + Allocator::free(entry); + + if (head_ == nullptr) { + tail_ = nullptr; + } + + return element; + } + + T* front() const { + if (head_ == nullptr) { + return nullptr; + } + + return head_->element; + } + + void clear() { + while (head_ != nullptr) { + LinkedListEntry<T>* p = head_; + head_ = head_->next; + Allocator::free(p); + } + + tail_ = nullptr; + } + + template<typename F> + void for_each(F action) const { + visit([&] (T* si) { + action(si); + return true; + }); + } + + template<typename F> + bool visit(F action) const { + for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) { + if (!action(e->element)) { + return false; + } + } + return true; + } + + template<typename F> + void remove_if(F predicate) { + for (LinkedListEntry<T>* e = head_, *p = nullptr; e != nullptr;) { + if (predicate(e->element)) { + LinkedListEntry<T>* next = e->next; + if (p == nullptr) { + head_ = next; + } else { + p->next = next; + } + + if (tail_ == e) { + tail_ = p; + } + + Allocator::free(e); + + e = next; + } else { + p = e; + e = e->next; + } + } + } + + template<typename F> + T* find_if(F predicate) const { + for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) { + if (predicate(e->element)) { + return e->element; + } + } + + return nullptr; + } + + iterator begin() const { + return iterator(head_); + } + + iterator end() const { + return iterator(nullptr); + } + + iterator find(T* value) const { + for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) { + if (e->element == value) { + return iterator(e); + } + } + + return end(); + } + + size_t copy_to_array(T* array[], size_t array_length) const { + size_t sz = 0; + for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) { + array[sz++] = e->element; + } + + return sz; + } + + bool contains(const T* el) const { + for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) { + if (e->element == el) { + return true; + } + } + return false; + } + + static LinkedList make_list(T* const element) { + LinkedList<T, Allocator> one_element_list; + one_element_list.push_back(element); + return one_element_list; + } + + private: + LinkedListEntry<T>* head_; + LinkedListEntry<T>* tail_; + DISALLOW_COPY_AND_ASSIGN(LinkedList); +}; + +#endif // __LINKED_LIST_H
diff --git a/linker/linker.cpp b/linker/linker.cpp new file mode 100644 index 0000000..d2a6e7d --- /dev/null +++ b/linker/linker.cpp
@@ -0,0 +1,3678 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <android/api-level.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <unistd.h> + +#include <new> +#include <string> +#include <unordered_map> +#include <vector> + +// Private C library headers. +#include "private/bionic_globals.h" +#include "private/bionic_tls.h" +#include "private/KernelArgumentBlock.h" +#include "private/ScopedPthreadMutexLocker.h" +#include "private/ScopeGuard.h" + +#include "linker.h" +#include "linker_block_allocator.h" +#include "linker_gdb_support.h" +#include "linker_globals.h" +#include "linker_debug.h" +#include "linker_dlwarning.h" +#include "linker_namespaces.h" +#include "linker_sleb128.h" +#include "linker_phdr.h" +#include "linker_relocs.h" +#include "linker_reloc_iterators.h" +#include "linker_utils.h" + +#include "android-base/strings.h" +#include "android-base/stringprintf.h" +#include "debuggerd/client.h" +#include "ziparchive/zip_archive.h" + +extern void __libc_init_globals(KernelArgumentBlock&); +extern void __libc_init_AT_SECURE(KernelArgumentBlock&); + +extern "C" void _start(); + +// Override macros to use C++ style casts. +#undef ELF_ST_TYPE +#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf) + +static android_namespace_t* g_anonymous_namespace = &g_default_namespace; + +static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); + +static LinkerTypeAllocator<soinfo> g_soinfo_allocator; +static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator; + +static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator; +static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator; + +static soinfo* solist; +static soinfo* sonext; +static soinfo* somain; // main process, always the one after libdl_info + +#if defined(__LP64__) +static const char* const kSystemLibDir = "/system/lib64"; +static const char* const kVendorLibDir = "/vendor/lib64"; +static const char* const kAsanSystemLibDir = "/data/lib64"; +static const char* const kAsanVendorLibDir = "/data/vendor/lib64"; +#else +static const char* const kSystemLibDir = "/system/lib"; +static const char* const kVendorLibDir = "/vendor/lib"; +static const char* const kAsanSystemLibDir = "/data/lib"; +static const char* const kAsanVendorLibDir = "/data/vendor/lib"; +#endif + +static const char* const kDefaultLdPaths[] = { + kSystemLibDir, + kVendorLibDir, + nullptr +}; + +static const char* const kAsanDefaultLdPaths[] = { + kAsanSystemLibDir, + kSystemLibDir, + kAsanVendorLibDir, + kVendorLibDir, + nullptr +}; + +// Is ASAN enabled? +static bool g_is_asan = false; + +static bool is_system_library(const std::string& realpath) { + for (const auto& dir : g_default_namespace.get_default_library_paths()) { + if (file_is_in_dir(realpath, dir)) { + return true; + } + } + return false; +} + +// Checks if the file exists and not a directory. +static bool file_exists(const char* path) { + struct stat s; + + if (stat(path, &s) != 0) { + return false; + } + + return S_ISREG(s.st_mode); +} + +// TODO(dimitry): The grey-list is a workaround for http://b/26394120 --- +// gradually remove libraries from this list until it is gone. +static bool is_greylisted(const char* name, const soinfo* needed_by) { + static const char* const kLibraryGreyList[] = { + "libandroid_runtime.so", + "libbinder.so", + "libcrypto.so", + "libcutils.so", + "libexpat.so", + "libgui.so", + "libmedia.so", + "libnativehelper.so", + "libskia.so", + "libssl.so", + "libstagefright.so", + "libsqlite.so", + "libui.so", + "libutils.so", + "libvorbisidec.so", + nullptr + }; + + // limit greylisting to apps targeting sdk version 23 and below + if (get_application_target_sdk_version() > 23) { + return false; + } + + // if the library needed by a system library - implicitly assume it + // is greylisted + + if (needed_by != nullptr && is_system_library(needed_by->get_realpath())) { + return true; + } + + // if this is an absolute path - make sure it points to /system/lib(64) + if (name[0] == '/' && dirname(name) == kSystemLibDir) { + // and reduce the path to basename + name = basename(name); + } + + for (size_t i = 0; kLibraryGreyList[i] != nullptr; ++i) { + if (strcmp(name, kLibraryGreyList[i]) == 0) { + return true; + } + } + + return false; +} +// END OF WORKAROUND + +static const char* const* g_default_ld_paths; +static std::vector<std::string> g_ld_preload_names; + +static std::vector<soinfo*> g_ld_preloads; + +static bool g_public_namespace_initialized; +static soinfo_list_t g_public_namespace; + +int g_ld_debug_verbosity; +abort_msg_t* g_abort_message = nullptr; // For debuggerd. + +#if STATS +struct linker_stats_t { + int count[kRelocMax]; +}; + +static linker_stats_t linker_stats; + +void count_relocation(RelocationKind kind) { + ++linker_stats.count[kind]; +} +#else +void count_relocation(RelocationKind) { +} +#endif + +#if COUNT_PAGES +uint32_t bitmask[4096]; +#endif + +static void notify_gdb_of_load(soinfo* info) { + if (info->is_linker() || info->is_main_executable()) { + // gdb already knows about the linker and the main executable. + return; + } + + link_map* map = &(info->link_map_head); + + map->l_addr = info->load_bias; + // link_map l_name field is not const. + map->l_name = const_cast<char*>(info->get_realpath()); + map->l_ld = info->dynamic; + + CHECK(map->l_name != nullptr); + CHECK(map->l_name[0] != '\0'); + + notify_gdb_of_load(map); +} + +static void notify_gdb_of_unload(soinfo* info) { + notify_gdb_of_unload(&(info->link_map_head)); +} + +LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() { + return g_soinfo_links_allocator.alloc(); +} + +void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) { + g_soinfo_links_allocator.free(entry); +} + +LinkedListEntry<android_namespace_t>* NamespaceListAllocator::alloc() { + return g_namespace_list_allocator.alloc(); +} + +void NamespaceListAllocator::free(LinkedListEntry<android_namespace_t>* entry) { + g_namespace_list_allocator.free(entry); +} + +static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name, + struct stat* file_stat, off64_t file_offset, + uint32_t rtld_flags) { + if (strlen(name) >= PATH_MAX) { + DL_ERR("library name \"%s\" too long", name); + return nullptr; + } + + soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(ns, name, file_stat, + file_offset, rtld_flags); + + sonext->next = si; + sonext = si; + + si->generate_handle(); + ns->add_soinfo(si); + + TRACE("name %s: allocated soinfo @ %p", name, si); + return si; +} + +static void soinfo_free(soinfo* si) { + if (si == nullptr) { + return; + } + + if (si->base != 0 && si->size != 0) { + if (!si->is_mapped_by_caller()) { + munmap(reinterpret_cast<void*>(si->base), si->size); + } else { + // remap the region as PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE + mmap(reinterpret_cast<void*>(si->base), si->size, PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + } + } + + soinfo *prev = nullptr, *trav; + + TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si); + + for (trav = solist; trav != nullptr; trav = trav->next) { + if (trav == si) { + break; + } + prev = trav; + } + + if (trav == nullptr) { + // si was not in solist + DL_ERR("name \"%s\"@%p is not in solist!", si->get_realpath(), si); + return; + } + + // clear links to/from si + si->remove_all_links(); + + // prev will never be null, because the first entry in solist is + // always the static libdl_info. + prev->next = si->next; + if (si == sonext) { + sonext = prev; + } + + si->~soinfo(); + g_soinfo_allocator.free(si); +} + +static void parse_path(const char* path, const char* delimiters, + std::vector<std::string>* resolved_paths) { + std::vector<std::string> paths; + split_path(path, delimiters, &paths); + resolve_paths(paths, resolved_paths); +} + +static void parse_LD_LIBRARY_PATH(const char* path) { + std::vector<std::string> ld_libary_paths; + parse_path(path, ":", &ld_libary_paths); + g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths)); +} + +static void parse_LD_PRELOAD(const char* path) { + g_ld_preload_names.clear(); + if (path != nullptr) { + // We have historically supported ':' as well as ' ' in LD_PRELOAD. + g_ld_preload_names = android::base::Split(path, " :"); + std::remove_if(g_ld_preload_names.begin(), + g_ld_preload_names.end(), + [] (const std::string& s) { return s.empty(); }); + } +} + +static bool realpath_fd(int fd, std::string* realpath) { + std::vector<char> buf(PATH_MAX), proc_self_fd(PATH_MAX); + __libc_format_buffer(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd); + if (readlink(&proc_self_fd[0], &buf[0], buf.size()) == -1) { + PRINT("readlink(\"%s\") failed: %s [fd=%d]", &proc_self_fd[0], strerror(errno), fd); + return false; + } + + *realpath = &buf[0]; + return true; +} + +#if defined(__arm__) + +// For a given PC, find the .so that it belongs to. +// Returns the base address of the .ARM.exidx section +// for that .so, and the number of 8-byte entries +// in that section (via *pcount). +// +// Intended to be called by libc's __gnu_Unwind_Find_exidx(). +// +// This function is exposed via dlfcn.cpp and libdl.so. +_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) { + uintptr_t addr = reinterpret_cast<uintptr_t>(pc); + + for (soinfo* si = solist; si != 0; si = si->next) { + if ((addr >= si->base) && (addr < (si->base + si->size))) { + *pcount = si->ARM_exidx_count; + return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx); + } + } + *pcount = 0; + return nullptr; +} + +#endif + +// Here, we only have to provide a callback to iterate across all the +// loaded libraries. gcc_eh does the rest. +int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) { + int rv = 0; + for (soinfo* si = solist; si != nullptr; si = si->next) { + dl_phdr_info dl_info; + dl_info.dlpi_addr = si->link_map_head.l_addr; + dl_info.dlpi_name = si->link_map_head.l_name; + dl_info.dlpi_phdr = si->phdr; + dl_info.dlpi_phnum = si->phnum; + rv = cb(&dl_info, sizeof(dl_phdr_info), data); + if (rv != 0) { + break; + } + } + return rv; +} + + +bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, + soinfo** si_found_in, const soinfo_list_t& global_group, + const soinfo_list_t& local_group, const ElfW(Sym)** symbol) { + SymbolName symbol_name(name); + const ElfW(Sym)* s = nullptr; + + /* "This element's presence in a shared object library alters the dynamic linker's + * symbol resolution algorithm for references within the library. Instead of starting + * a symbol search with the executable file, the dynamic linker starts from the shared + * object itself. If the shared object fails to supply the referenced symbol, the + * dynamic linker then searches the executable file and other shared objects as usual." + * + * http://www.sco.com/developers/gabi/2012-12-31/ch5.dynamic.html + * + * Note that this is unlikely since static linker avoids generating + * relocations for -Bsymbolic linked dynamic executables. + */ + if (si_from->has_DT_SYMBOLIC) { + DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_realpath(), name); + if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) { + return false; + } + + if (s != nullptr) { + *si_found_in = si_from; + } + } + + // 1. Look for it in global_group + if (s == nullptr) { + bool error = false; + global_group.visit([&](soinfo* global_si) { + DEBUG("%s: looking up %s in %s (from global group)", + si_from->get_realpath(), name, global_si->get_realpath()); + if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) { + error = true; + return false; + } + + if (s != nullptr) { + *si_found_in = global_si; + return false; + } + + return true; + }); + + if (error) { + return false; + } + } + + // 2. Look for it in the local group + if (s == nullptr) { + bool error = false; + local_group.visit([&](soinfo* local_si) { + if (local_si == si_from && si_from->has_DT_SYMBOLIC) { + // we already did this - skip + return true; + } + + DEBUG("%s: looking up %s in %s (from local group)", + si_from->get_realpath(), name, local_si->get_realpath()); + if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) { + error = true; + return false; + } + + if (s != nullptr) { + *si_found_in = local_si; + return false; + } + + return true; + }); + + if (error) { + return false; + } + } + + if (s != nullptr) { + TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, " + "found in %s, base = %p, load bias = %p", + si_from->get_realpath(), name, reinterpret_cast<void*>(s->st_value), + (*si_found_in)->get_realpath(), reinterpret_cast<void*>((*si_found_in)->base), + reinterpret_cast<void*>((*si_found_in)->load_bias)); + } + + *symbol = s; + return true; +} + +class ProtectedDataGuard { + public: + ProtectedDataGuard() { + if (ref_count_++ == 0) { + protect_data(PROT_READ | PROT_WRITE); + } + } + + ~ProtectedDataGuard() { + if (ref_count_ == 0) { // overflow + __libc_fatal("Too many nested calls to dlopen()"); + } + + if (--ref_count_ == 0) { + protect_data(PROT_READ); + } + } + private: + void protect_data(int protection) { + g_soinfo_allocator.protect_all(protection); + g_soinfo_links_allocator.protect_all(protection); + g_namespace_allocator.protect_all(protection); + g_namespace_list_allocator.protect_all(protection); + } + + static size_t ref_count_; +}; + +size_t ProtectedDataGuard::ref_count_ = 0; + +// Each size has it's own allocator. +template<size_t size> +class SizeBasedAllocator { + public: + static void* alloc() { + return allocator_.alloc(); + } + + static void free(void* ptr) { + allocator_.free(ptr); + } + + private: + static LinkerBlockAllocator allocator_; +}; + +template<size_t size> +LinkerBlockAllocator SizeBasedAllocator<size>::allocator_(size); + +template<typename T> +class TypeBasedAllocator { + public: + static T* alloc() { + return reinterpret_cast<T*>(SizeBasedAllocator<sizeof(T)>::alloc()); + } + + static void free(T* ptr) { + SizeBasedAllocator<sizeof(T)>::free(ptr); + } +}; + +class LoadTask { + public: + struct deleter_t { + void operator()(LoadTask* t) { + t->~LoadTask(); + TypeBasedAllocator<LoadTask>::free(t); + } + }; + + static deleter_t deleter; + + static LoadTask* create(const char* name, soinfo* needed_by, + std::unordered_map<const soinfo*, ElfReader>* readers_map) { + LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc(); + return new (ptr) LoadTask(name, needed_by, readers_map); + } + + const char* get_name() const { + return name_; + } + + soinfo* get_needed_by() const { + return needed_by_; + } + + soinfo* get_soinfo() const { + return si_; + } + + void set_soinfo(soinfo* si) { + si_ = si; + } + + off64_t get_file_offset() const { + return file_offset_; + } + + void set_file_offset(off64_t offset) { + file_offset_ = offset; + } + + int get_fd() const { + return fd_; + } + + void set_fd(int fd, bool assume_ownership) { + fd_ = fd; + close_fd_ = assume_ownership; + } + + const android_dlextinfo* get_extinfo() const { + return extinfo_; + } + + void set_extinfo(const android_dlextinfo* extinfo) { + extinfo_ = extinfo; + } + + bool is_dt_needed() const { + return is_dt_needed_; + } + + void set_dt_needed(bool is_dt_needed) { + is_dt_needed_ = is_dt_needed; + } + + const ElfReader& get_elf_reader() const { + CHECK(si_ != nullptr); + return (*elf_readers_map_)[si_]; + } + + ElfReader& get_elf_reader() { + CHECK(si_ != nullptr); + return (*elf_readers_map_)[si_]; + } + + std::unordered_map<const soinfo*, ElfReader>* get_readers_map() { + return elf_readers_map_; + } + + bool read(const char* realpath, off64_t file_size) { + ElfReader& elf_reader = get_elf_reader(); + return elf_reader.Read(realpath, fd_, file_offset_, file_size); + } + + bool load() { + ElfReader& elf_reader = get_elf_reader(); + if (!elf_reader.Load(extinfo_)) { + return false; + } + + si_->base = elf_reader.load_start(); + si_->size = elf_reader.load_size(); + si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller()); + si_->load_bias = elf_reader.load_bias(); + si_->phnum = elf_reader.phdr_count(); + si_->phdr = elf_reader.loaded_phdr(); + + return true; + } + + private: + LoadTask(const char* name, soinfo* needed_by, + std::unordered_map<const soinfo*, ElfReader>* readers_map) + : name_(name), needed_by_(needed_by), si_(nullptr), + fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map), + is_dt_needed_(false) {} + + ~LoadTask() { + if (fd_ != -1 && close_fd_) { + close(fd_); + } + } + + const char* name_; + soinfo* needed_by_; + soinfo* si_; + const android_dlextinfo* extinfo_; + int fd_; + bool close_fd_; + off64_t file_offset_; + std::unordered_map<const soinfo*, ElfReader>* elf_readers_map_; + // TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list) + bool is_dt_needed_; + // END OF WORKAROUND + + DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask); +}; + +LoadTask::deleter_t LoadTask::deleter; + +template <typename T> +using linked_list_t = LinkedList<T, TypeBasedAllocator<LinkedListEntry<T>>>; + +typedef linked_list_t<soinfo> SoinfoLinkedList; +typedef linked_list_t<const char> StringLinkedList; +typedef std::vector<LoadTask*> LoadTaskList; + + +// This function walks down the tree of soinfo dependencies +// in breadth-first order and +// * calls action(soinfo* si) for each node, and +// * terminates walk if action returns false. +// +// walk_dependencies_tree returns false if walk was terminated +// by the action and true otherwise. +template<typename F> +static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_size, F action) { + SoinfoLinkedList visit_list; + SoinfoLinkedList visited; + + for (size_t i = 0; i < root_soinfos_size; ++i) { + visit_list.push_back(root_soinfos[i]); + } + + soinfo* si; + while ((si = visit_list.pop_front()) != nullptr) { + if (visited.contains(si)) { + continue; + } + + if (!action(si)) { + return false; + } + + visited.push_back(si); + + si->get_children().for_each([&](soinfo* child) { + visit_list.push_back(child); + }); + } + + return true; +} + + +static const ElfW(Sym)* dlsym_handle_lookup(soinfo* root, soinfo* skip_until, + soinfo** found, SymbolName& symbol_name, + const version_info* vi) { + const ElfW(Sym)* result = nullptr; + bool skip_lookup = skip_until != nullptr; + + walk_dependencies_tree(&root, 1, [&](soinfo* current_soinfo) { + if (skip_lookup) { + skip_lookup = current_soinfo != skip_until; + return true; + } + + if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) { + result = nullptr; + return false; + } + + if (result != nullptr) { + *found = current_soinfo; + return false; + } + + return true; + }); + + return result; +} + +static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, + const char* name, + const version_info* vi, + soinfo** found, + soinfo* caller, + void* handle); + +// This is used by dlsym(3). It performs symbol lookup only within the +// specified soinfo object and its dependencies in breadth first order. +static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, + const char* name, const version_info* vi) { + // According to man dlopen(3) and posix docs in the case when si is handle + // of the main executable we need to search not only in the executable and its + // dependencies but also in all libraries loaded with RTLD_GLOBAL. + // + // Since RTLD_GLOBAL is always set for the main executable and all dt_needed shared + // libraries and they are loaded in breath-first (correct) order we can just execute + // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup. + if (si == somain) { + return dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT); + } + + SymbolName symbol_name(name); + return dlsym_handle_lookup(si, nullptr, found, symbol_name, vi); +} + +/* This is used by dlsym(3) to performs a global symbol lookup. If the + start value is null (for RTLD_DEFAULT), the search starts at the + beginning of the global solist. Otherwise the search starts at the + specified soinfo (for RTLD_NEXT). + */ +static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, + const char* name, + const version_info* vi, + soinfo** found, + soinfo* caller, + void* handle) { + SymbolName symbol_name(name); + + auto& soinfo_list = ns->soinfo_list(); + auto start = soinfo_list.begin(); + + if (handle == RTLD_NEXT) { + if (caller == nullptr) { + return nullptr; + } else { + auto it = soinfo_list.find(caller); + CHECK (it != soinfo_list.end()); + start = ++it; + } + } + + const ElfW(Sym)* s = nullptr; + for (auto it = start, end = soinfo_list.end(); it != end; ++it) { + soinfo* si = *it; + // Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...) + // if the library is opened by application with target api level <= 22 + // See http://b/21565766 + if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0 && si->get_target_sdk_version() > 22) { + continue; + } + + if (!si->find_symbol_by_name(symbol_name, vi, &s)) { + return nullptr; + } + + if (s != nullptr) { + *found = si; + break; + } + } + + // If not found - use dlsym_handle_lookup for caller's + // local_group unless it is part of the global group in which + // case we already did it. + if (s == nullptr && caller != nullptr && + (caller->get_rtld_flags() & RTLD_GLOBAL) == 0) { + return dlsym_handle_lookup(caller->get_local_group_root(), + (handle == RTLD_NEXT) ? caller : nullptr, found, symbol_name, vi); + } + + if (s != nullptr) { + TRACE_TYPE(LOOKUP, "%s s->st_value = %p, found->base = %p", + name, reinterpret_cast<void*>(s->st_value), reinterpret_cast<void*>((*found)->base)); + } + + return s; +} + +soinfo* find_containing_library(const void* p) { + ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p); + for (soinfo* si = solist; si != nullptr; si = si->next) { + if (address >= si->base && address - si->base < si->size) { + return si; + } + } + return nullptr; +} + +class ZipArchiveCache { + public: + ZipArchiveCache() {} + ~ZipArchiveCache(); + + bool get_or_open(const char* zip_path, ZipArchiveHandle* handle); + private: + DISALLOW_COPY_AND_ASSIGN(ZipArchiveCache); + + std::unordered_map<std::string, ZipArchiveHandle> cache_; +}; + +bool ZipArchiveCache::get_or_open(const char* zip_path, ZipArchiveHandle* handle) { + std::string key(zip_path); + + auto it = cache_.find(key); + if (it != cache_.end()) { + *handle = it->second; + return true; + } + + int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + return false; + } + + if (OpenArchiveFd(fd, "", handle) != 0) { + // invalid zip-file (?) + CloseArchive(handle); + close(fd); + return false; + } + + cache_[key] = *handle; + return true; +} + +ZipArchiveCache::~ZipArchiveCache() { + for (const auto& it : cache_) { + CloseArchive(it.second); + } +} + +static int open_library_in_zipfile(ZipArchiveCache* zip_archive_cache, + const char* const input_path, + off64_t* file_offset, std::string* realpath) { + std::string normalized_path; + if (!normalize_path(input_path, &normalized_path)) { + return -1; + } + + const char* const path = normalized_path.c_str(); + TRACE("Trying zip file open from path \"%s\" -> normalized \"%s\"", input_path, path); + + // Treat an '!/' separator inside a path as the separator between the name + // of the zip file on disk and the subdirectory to search within it. + // For example, if path is "foo.zip!/bar/bas/x.so", then we search for + // "bar/bas/x.so" within "foo.zip". + const char* const separator = strstr(path, kZipFileSeparator); + if (separator == nullptr) { + return -1; + } + + char buf[512]; + if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) { + PRINT("Warning: ignoring very long library path: %s", path); + return -1; + } + + buf[separator - path] = '\0'; + + const char* zip_path = buf; + const char* file_path = &buf[separator - path + 2]; + int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + return -1; + } + + ZipArchiveHandle handle; + if (!zip_archive_cache->get_or_open(zip_path, &handle)) { + // invalid zip-file (?) + close(fd); + return -1; + } + + ZipEntry entry; + + if (FindEntry(handle, ZipString(file_path), &entry) != 0) { + // Entry was not found. + close(fd); + return -1; + } + + // Check if it is properly stored + if (entry.method != kCompressStored || (entry.offset % PAGE_SIZE) != 0) { + close(fd); + return -1; + } + + *file_offset = entry.offset; + + if (realpath_fd(fd, realpath)) { + *realpath += separator; + } else { + PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", + normalized_path.c_str()); + *realpath = normalized_path; + } + + return fd; +} + +static bool format_path(char* buf, size_t buf_size, const char* path, const char* name) { + int n = __libc_format_buffer(buf, buf_size, "%s/%s", path, name); + if (n < 0 || n >= static_cast<int>(buf_size)) { + PRINT("Warning: ignoring very long library path: %s/%s", path, name); + return false; + } + + return true; +} + +static int open_library_on_paths(ZipArchiveCache* zip_archive_cache, + const char* name, off64_t* file_offset, + const std::vector<std::string>& paths, + std::string* realpath) { + for (const auto& path : paths) { + char buf[512]; + if (!format_path(buf, sizeof(buf), path.c_str(), name)) { + continue; + } + + int fd = -1; + if (strstr(buf, kZipFileSeparator) != nullptr) { + fd = open_library_in_zipfile(zip_archive_cache, buf, file_offset, realpath); + } + + if (fd == -1) { + fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + *file_offset = 0; + if (!realpath_fd(fd, realpath)) { + PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", buf); + *realpath = buf; + } + } + } + + if (fd != -1) { + return fd; + } + } + + return -1; +} + +static int open_library(android_namespace_t* ns, + ZipArchiveCache* zip_archive_cache, + const char* name, soinfo *needed_by, + off64_t* file_offset, std::string* realpath) { + TRACE("[ opening %s ]", name); + + // If the name contains a slash, we should attempt to open it directly and not search the paths. + if (strchr(name, '/') != nullptr) { + int fd = -1; + + if (strstr(name, kZipFileSeparator) != nullptr) { + fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath); + } + + if (fd == -1) { + fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + *file_offset = 0; + if (!realpath_fd(fd, realpath)) { + PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name); + *realpath = name; + } + } + } + + return fd; + } + + // Otherwise we try LD_LIBRARY_PATH first, and fall back to the default library path + int fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_ld_library_paths(), realpath); + if (fd == -1 && needed_by != nullptr) { + fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath); + // Check if the library is accessible + if (fd != -1 && !ns->is_accessible(*realpath)) { + fd = -1; + } + } + + if (fd == -1) { + fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath); + } + + // TODO(dimitry): workaround for http://b/26394120 (the grey-list) + if (fd == -1 && ns != &g_default_namespace && is_greylisted(name, needed_by)) { + // try searching for it on default_namespace default_library_path + fd = open_library_on_paths(zip_archive_cache, name, file_offset, + g_default_namespace.get_default_library_paths(), realpath); + } + // END OF WORKAROUND + + return fd; +} + +static const char* fix_dt_needed(const char* dt_needed, const char* sopath __unused) { +#if !defined(__LP64__) + // Work around incorrect DT_NEEDED entries for old apps: http://b/21364029 + if (get_application_target_sdk_version() <= 22) { + const char* bname = basename(dt_needed); + if (bname != dt_needed) { + DL_WARN("library \"%s\" has invalid DT_NEEDED entry \"%s\"", sopath, dt_needed); + add_dlwarning(sopath, "invalid DT_NEEDED entry", dt_needed); + } + + return bname; + } +#endif + return dt_needed; +} + +template<typename F> +static void for_each_dt_needed(const soinfo* si, F action) { + for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_NEEDED) { + action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath())); + } + } +} + +template<typename F> +static void for_each_dt_needed(const ElfReader& elf_reader, F action) { + for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_NEEDED) { + action(fix_dt_needed(elf_reader.get_string(d->d_un.d_val), elf_reader.name())); + } + } +} + +static bool load_library(android_namespace_t* ns, + LoadTask* task, + LoadTaskList* load_tasks, + int rtld_flags, + const std::string& realpath) { + off64_t file_offset = task->get_file_offset(); + const char* name = task->get_name(); + const android_dlextinfo* extinfo = task->get_extinfo(); + + if ((file_offset % PAGE_SIZE) != 0) { + DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset); + return false; + } + if (file_offset < 0) { + DL_ERR("file offset for the library \"%s\" is negative: %" PRId64, name, file_offset); + return false; + } + + struct stat file_stat; + if (TEMP_FAILURE_RETRY(fstat(task->get_fd(), &file_stat)) != 0) { + DL_ERR("unable to stat file for the library \"%s\": %s", name, strerror(errno)); + return false; + } + if (file_offset >= file_stat.st_size) { + DL_ERR("file offset for the library \"%s\" >= file size: %" PRId64 " >= %" PRId64, + name, file_offset, file_stat.st_size); + return false; + } + + // Check for symlink and other situations where + // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set + if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) { + auto predicate = [&](soinfo* si) { + return 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_file_offset() == file_offset; + }; + + soinfo* si = ns->soinfo_list().find_if(predicate); + + // check public namespace + if (si == nullptr) { + si = g_public_namespace.find_if(predicate); + if (si != nullptr) { + ns->add_soinfo(si); + } + } + + if (si != nullptr) { + TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " + "will return existing soinfo", name, si->get_realpath()); + task->set_soinfo(si); + return true; + } + } + + if ((rtld_flags & RTLD_NOLOAD) != 0) { + DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name); + return false; + } + + if (!ns->is_accessible(realpath)) { + // TODO(dimitry): workaround for http://b/26394120 - the grey-list + const soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr; + if (is_greylisted(name, needed_by)) { + // print warning only if needed by non-system library + if (needed_by == nullptr || !is_system_library(needed_by->get_realpath())) { + const soinfo* needed_or_dlopened_by = task->get_needed_by(); + const char* sopath = needed_or_dlopened_by == nullptr ? "(unknown)" : + needed_or_dlopened_by->get_realpath(); + DL_WARN("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the namespace \"%s\"" + " - the access is temporarily granted as a workaround for http://b/26394120, note that the access" + " will be removed in future releases of Android.", + name, realpath.c_str(), sopath, ns->get_name()); + add_dlwarning(sopath, "unauthorized access to", name); + } + } else { + // do not load libraries if they are not accessible for the specified namespace. + const char* needed_or_dlopened_by = task->get_needed_by() == nullptr ? + "(unknown)" : + task->get_needed_by()->get_realpath(); + + DL_ERR("library \"%s\" needed or dlopened by \"%s\" is not accessible for the namespace \"%s\"", + name, needed_or_dlopened_by, ns->get_name()); + + PRINT("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the" + " namespace: [name=\"%s\", ld_library_paths=\"%s\", default_library_paths=\"%s\"," + " permitted_paths=\"%s\"]", + name, realpath.c_str(), + needed_or_dlopened_by, + ns->get_name(), + android::base::Join(ns->get_ld_library_paths(), ':').c_str(), + android::base::Join(ns->get_default_library_paths(), ':').c_str(), + android::base::Join(ns->get_permitted_paths(), ':').c_str()); + return false; + } + } + + soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags); + if (si == nullptr) { + return false; + } + + task->set_soinfo(si); + + // Read the ELF header and some of the segments. + if (!task->read(realpath.c_str(), file_stat.st_size)) { + soinfo_free(si); + task->set_soinfo(nullptr); + return false; + } + + // find and set DT_RUNPATH and dt_soname + // Note that these field values are temporary and are + // going to be overwritten on soinfo::prelink_image + // with values from PT_LOAD segments. + const ElfReader& elf_reader = task->get_elf_reader(); + for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_RUNPATH) { + si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val)); + } + if (d->d_tag == DT_SONAME) { + si->set_soname(elf_reader.get_string(d->d_un.d_val)); + } + } + + for_each_dt_needed(task->get_elf_reader(), [&](const char* name) { + load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map())); + }); + + return true; +} + +static bool load_library(android_namespace_t* ns, + LoadTask* task, + ZipArchiveCache* zip_archive_cache, + LoadTaskList* load_tasks, + int rtld_flags) { + const char* name = task->get_name(); + soinfo* needed_by = task->get_needed_by(); + const android_dlextinfo* extinfo = task->get_extinfo(); + + off64_t file_offset; + std::string realpath; + if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) { + file_offset = 0; + if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) { + file_offset = extinfo->library_fd_offset; + } + + if (!realpath_fd(extinfo->library_fd, &realpath)) { + PRINT("warning: unable to get realpath for the library \"%s\" by extinfo->library_fd. " + "Will use given name.", name); + realpath = name; + } + + task->set_fd(extinfo->library_fd, false); + task->set_file_offset(file_offset); + return load_library(ns, task, load_tasks, rtld_flags, realpath); + } + + // Open the file. + int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath); + if (fd == -1) { + DL_ERR("library \"%s\" not found", name); + return false; + } + + task->set_fd(fd, true); + task->set_file_offset(file_offset); + + return load_library(ns, task, load_tasks, rtld_flags, realpath); +} + +// Returns true if library was found and false in 2 cases +// 1. (for default namespace only) The library was found but loaded under different +// target_sdk_version (*candidate != nullptr) +// 2. The library was not found by soname (*candidate is nullptr) +static bool find_loaded_library_by_soname(android_namespace_t* ns, + const char* name, soinfo** candidate) { + *candidate = nullptr; + + // Ignore filename with path. + if (strchr(name, '/') != nullptr) { + return false; + } + + uint32_t target_sdk_version = get_application_target_sdk_version(); + + return !ns->soinfo_list().visit([&](soinfo* si) { + const char* soname = si->get_soname(); + if (soname != nullptr && (strcmp(name, soname) == 0)) { + // If the library was opened under different target sdk version + // skip this step and try to reopen it. The exceptions are + // "libdl.so" and global group. There is no point in skipping + // them because relocation process is going to use them + // in any case. + bool is_libdl = si == solist; + if (is_libdl || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0 || + !si->is_linked() || si->get_target_sdk_version() == target_sdk_version || + ns != &g_default_namespace) { + *candidate = si; + return false; + } else if (*candidate == nullptr) { + // for the different sdk version in the default namespace + // remember the first library. + *candidate = si; + } + } + + return true; + }); +} + +static bool find_library_internal(android_namespace_t* ns, + LoadTask* task, + ZipArchiveCache* zip_archive_cache, + LoadTaskList* load_tasks, + int rtld_flags) { + soinfo* candidate; + + if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) { + task->set_soinfo(candidate); + return true; + } + + if (ns != &g_default_namespace) { + // check public namespace + candidate = g_public_namespace.find_if([&](soinfo* si) { + return strcmp(task->get_name(), si->get_soname()) == 0; + }); + + if (candidate != nullptr) { + ns->add_soinfo(candidate); + task->set_soinfo(candidate); + return true; + } + } + + // Library might still be loaded, the accurate detection + // of this fact is done by load_library. + TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]", + task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); + + if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) { + return true; + } else { + // In case we were unable to load the library but there + // is a candidate loaded under the same soname but different + // sdk level - return it anyways. + if (candidate != nullptr) { + task->set_soinfo(candidate); + return true; + } + } + + return false; +} + +static void soinfo_unload(soinfo* si); +static void soinfo_unload(soinfo* soinfos[], size_t count); + +// TODO: this is slightly unusual way to construct +// the global group for relocation. Not every RTLD_GLOBAL +// library is included in this group for backwards-compatibility +// reasons. +// +// This group consists of the main executable, LD_PRELOADs +// and libraries with the DF_1_GLOBAL flag set. +static soinfo_list_t make_global_group(android_namespace_t* ns) { + soinfo_list_t global_group; + ns->soinfo_list().for_each([&](soinfo* si) { + if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { + global_group.push_back(si); + } + }); + + return global_group; +} + +// This function provides a list of libraries to be shared +// by the namespace. For the default namespace this is the global +// group (see make_global_group). For all others this is a group +// of RTLD_GLOBAL libraries (which includes the global group from +// the default namespace). +static soinfo_list_t get_shared_group(android_namespace_t* ns) { + if (ns == &g_default_namespace) { + return make_global_group(ns); + } + + soinfo_list_t shared_group; + ns->soinfo_list().for_each([&](soinfo* si) { + if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) { + shared_group.push_back(si); + } + }); + + return shared_group; +} + +static void shuffle(std::vector<LoadTask*>* v) { + for (size_t i = 0, size = v->size(); i < size; ++i) { + size_t n = size - i; + size_t r = arc4random_uniform(n); + std::swap((*v)[n-1], (*v)[r]); + } +} + +// add_as_children - add first-level loaded libraries (i.e. library_names[], but +// not their transitive dependencies) as children of the start_with library. +// This is false when find_libraries is called for dlopen(), when newly loaded +// libraries must form a disjoint tree. +static bool find_libraries(android_namespace_t* ns, + soinfo* start_with, + const char* const library_names[], + size_t library_names_count, soinfo* soinfos[], + std::vector<soinfo*>* ld_preloads, + size_t ld_preloads_count, int rtld_flags, + const android_dlextinfo* extinfo, + bool add_as_children) { + // Step 0: prepare. + LoadTaskList load_tasks; + std::unordered_map<const soinfo*, ElfReader> readers_map; + + for (size_t i = 0; i < library_names_count; ++i) { + const char* name = library_names[i]; + load_tasks.push_back(LoadTask::create(name, start_with, &readers_map)); + } + + // Construct global_group. + soinfo_list_t global_group = make_global_group(ns); + + // If soinfos array is null allocate one on stack. + // The array is needed in case of failure; for example + // when library_names[] = {libone.so, libtwo.so} and libone.so + // is loaded correctly but libtwo.so failed for some reason. + // In this case libone.so should be unloaded on return. + // See also implementation of failure_guard below. + + if (soinfos == nullptr) { + size_t soinfos_size = sizeof(soinfo*)*library_names_count; + soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size)); + memset(soinfos, 0, soinfos_size); + } + + // list of libraries to link - see step 2. + size_t soinfos_count = 0; + + auto scope_guard = make_scope_guard([&]() { + for (LoadTask* t : load_tasks) { + LoadTask::deleter(t); + } + }); + + auto failure_guard = make_scope_guard([&]() { + // Housekeeping + soinfo_unload(soinfos, soinfos_count); + }); + + ZipArchiveCache zip_archive_cache; + + // Step 1: expand the list of load_tasks to include + // all DT_NEEDED libraries (do not load them just yet) + for (size_t i = 0; i<load_tasks.size(); ++i) { + LoadTask* task = load_tasks[i]; + soinfo* needed_by = task->get_needed_by(); + + bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children); + task->set_extinfo(is_dt_needed ? nullptr : extinfo); + task->set_dt_needed(is_dt_needed); + + if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) { + return false; + } + + soinfo* si = task->get_soinfo(); + + if (is_dt_needed) { + needed_by->add_child(si); + } + + if (si->is_linked()) { + si->increment_ref_count(); + } + + // When ld_preloads is not null, the first + // ld_preloads_count libs are in fact ld_preloads. + if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) { + ld_preloads->push_back(si); + } + + if (soinfos_count < library_names_count) { + soinfos[soinfos_count++] = si; + } + } + + // Step 2: Load libraries in random order (see b/24047022) + LoadTaskList load_list; + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + auto pred = [&](const LoadTask* t) { + return t->get_soinfo() == si; + }; + + if (!si->is_linked() && + std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) { + load_list.push_back(task); + } + } + shuffle(&load_list); + + for (auto&& task : load_list) { + if (!task->load()) { + return false; + } + } + + // Step 3: pre-link all DT_NEEDED libraries in breadth first order. + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (!si->is_linked() && !si->prelink_image()) { + return false; + } + } + + // Step 4: Add LD_PRELOADed libraries to the global group for + // future runs. There is no need to explicitly add them to + // the global group for this run because they are going to + // appear in the local group in the correct order. + if (ld_preloads != nullptr) { + for (auto&& si : *ld_preloads) { + si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); + } + } + + + // Step 5: link libraries. + soinfo_list_t local_group; + walk_dependencies_tree( + (start_with != nullptr && add_as_children) ? &start_with : soinfos, + (start_with != nullptr && add_as_children) ? 1 : soinfos_count, + [&] (soinfo* si) { + local_group.push_back(si); + return true; + }); + + // We need to increment ref_count in case + // the root of the local group was not linked. + bool was_local_group_root_linked = local_group.front()->is_linked(); + + bool linked = local_group.visit([&](soinfo* si) { + if (!si->is_linked()) { + if (!si->link_image(global_group, local_group, extinfo)) { + return false; + } + } + + return true; + }); + + if (linked) { + local_group.for_each([](soinfo* si) { + if (!si->is_linked()) { + si->set_linked(); + } + }); + + failure_guard.disable(); + } + + if (!was_local_group_root_linked) { + local_group.front()->increment_ref_count(); + } + + return linked; +} + +static soinfo* find_library(android_namespace_t* ns, + const char* name, int rtld_flags, + const android_dlextinfo* extinfo, + soinfo* needed_by) { + soinfo* si; + + if (name == nullptr) { + si = somain; + } else if (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags, + extinfo, /* add_as_children */ false)) { + return nullptr; + } + + return si; +} + +static void soinfo_unload(soinfo* root) { + if (root->is_linked()) { + root = root->get_local_group_root(); + } + + if (!root->can_unload()) { + TRACE("not unloading \"%s\" - the binary is flagged with NODELETE", root->get_realpath()); + return; + } + + soinfo_unload(&root, 1); +} + +static void soinfo_unload(soinfo* soinfos[], size_t count) { + // Note that the library can be loaded but not linked; + // in which case there is no root but we still need + // to walk the tree and unload soinfos involved. + // + // This happens on unsuccessful dlopen, when one of + // the DT_NEEDED libraries could not be linked/found. + if (count == 0) { + return; + } + + soinfo_list_t unload_list; + for (size_t i = 0; i < count; ++i) { + soinfo* si = soinfos[i]; + + if (si->can_unload()) { + size_t ref_count = si->is_linked() ? si->decrement_ref_count() : 0; + if (ref_count == 0) { + unload_list.push_back(si); + } else { + TRACE("not unloading '%s' group, decrementing ref_count to %zd", + si->get_realpath(), ref_count); + } + } else { + TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->get_realpath()); + return; + } + } + + // This is used to identify soinfos outside of the load-group + // note that we cannot have > 1 in the array and have any of them + // linked. This is why we can safely use the first one. + soinfo* root = soinfos[0]; + + soinfo_list_t local_unload_list; + soinfo_list_t external_unload_list; + soinfo* si = nullptr; + + while ((si = unload_list.pop_front()) != nullptr) { + if (local_unload_list.contains(si)) { + continue; + } + + local_unload_list.push_back(si); + + if (si->has_min_version(0)) { + soinfo* child = nullptr; + while ((child = si->get_children().pop_front()) != nullptr) { + TRACE("%s@%p needs to unload %s@%p", si->get_realpath(), si, + child->get_realpath(), child); + + if (local_unload_list.contains(child)) { + continue; + } else if (child->is_linked() && child->get_local_group_root() != root) { + external_unload_list.push_back(child); + } else { + unload_list.push_front(child); + } + } + } else { +#if !defined(__work_around_b_24465209__) + __libc_fatal("soinfo for \"%s\"@%p has no version", si->get_realpath(), si); +#else + PRINT("warning: soinfo for \"%s\"@%p has no version", si->get_realpath(), si); + for_each_dt_needed(si, [&] (const char* library_name) { + TRACE("deprecated (old format of soinfo): %s needs to unload %s", + si->get_realpath(), library_name); + + soinfo* needed = find_library(si->get_primary_namespace(), + library_name, RTLD_NOLOAD, nullptr, nullptr); + + if (needed != nullptr) { + // Not found: for example if symlink was deleted between dlopen and dlclose + // Since we cannot really handle errors at this point - print and continue. + PRINT("warning: couldn't find %s needed by %s on unload.", + library_name, si->get_realpath()); + return; + } else if (local_unload_list.contains(needed)) { + // already visited + return; + } else if (needed->is_linked() && needed->get_local_group_root() != root) { + // external group + external_unload_list.push_back(needed); + } else { + // local group + unload_list.push_front(needed); + } + }); +#endif + } + } + + local_unload_list.for_each([](soinfo* si) { + si->call_destructors(); + }); + + while ((si = local_unload_list.pop_front()) != nullptr) { + notify_gdb_of_unload(si); + soinfo_free(si); + } + + while ((si = external_unload_list.pop_front()) != nullptr) { + soinfo_unload(si); + } +} + +static std::string symbol_display_name(const char* sym_name, const char* sym_ver) { + if (sym_ver == nullptr) { + return sym_name; + } + + return std::string(sym_name) + ", version " + sym_ver; +} + +static android_namespace_t* get_caller_namespace(soinfo* caller) { + return caller != nullptr ? caller->get_primary_namespace() : g_anonymous_namespace; +} + +void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { + // Use basic string manipulation calls to avoid snprintf. + // snprintf indirectly calls pthread_getspecific to get the size of a buffer. + // When debug malloc is enabled, this call returns 0. This in turn causes + // snprintf to do nothing, which causes libraries to fail to load. + // See b/17302493 for further details. + // Once the above bug is fixed, this code can be modified to use + // snprintf again. + size_t required_len = 0; + for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) { + required_len += strlen(g_default_ld_paths[i]) + 1; + } + if (buffer_size < required_len) { + __libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: " + "buffer len %zu, required len %zu", buffer_size, required_len); + } + char* end = buffer; + for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) { + if (i > 0) *end++ = ':'; + end = stpcpy(end, g_default_ld_paths[i]); + } +} + +void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path) { + parse_LD_LIBRARY_PATH(ld_library_path); +} + +static std::string android_dlextinfo_to_string(const android_dlextinfo* info) { + if (info == nullptr) { + return "(null)"; + } + + return android::base::StringPrintf("[flags=0x%" PRIx64 "," + " reserved_addr=%p," + " reserved_size=0x%zx," + " relro_fd=%d," + " library_fd=%d," + " library_fd_offset=0x%" PRIx64 "," + " library_namespace=%s@%p]", + info->flags, + info->reserved_addr, + info->reserved_size, + info->relro_fd, + info->library_fd, + info->library_fd_offset, + (info->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0 ? + (info->library_namespace != nullptr ? + info->library_namespace->get_name() : "(null)") : "(n/a)", + (info->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0 ? + info->library_namespace : nullptr); +} + +void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, + void* caller_addr) { + soinfo* const caller = find_containing_library(caller_addr); + android_namespace_t* ns = get_caller_namespace(caller); + + LD_LOG(kLogDlopen, + "dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p) ...", + name, + flags, + android_dlextinfo_to_string(extinfo).c_str(), + caller == nullptr ? "(null)" : caller->get_realpath(), + ns == nullptr ? "(null)" : ns->get_name(), + ns); + + auto failure_guard = make_scope_guard([&]() { + LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer()); + }); + + if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) { + DL_ERR("invalid flags to dlopen: %x", 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_FD_OFFSET) != 0) { + DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without " + "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags); + return nullptr; + } + + if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 && + (extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) { + DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not " + "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT"); + return nullptr; + } + + if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) { + if (extinfo->library_namespace == nullptr) { + DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null"); + return nullptr; + } + ns = extinfo->library_namespace; + } + } + + std::string asan_name_holder; + + const char* translated_name = name; + if (g_is_asan) { + if (file_is_in_dir(name, kSystemLibDir)) { + asan_name_holder = std::string(kAsanSystemLibDir) + "/" + basename(name); + if (file_exists(asan_name_holder.c_str())) { + translated_name = asan_name_holder.c_str(); + PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name); + } + } else if (file_is_in_dir(name, kVendorLibDir)) { + asan_name_holder = std::string(kAsanVendorLibDir) + "/" + basename(name); + if (file_exists(asan_name_holder.c_str())) { + translated_name = asan_name_holder.c_str(); + PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name); + } + } + } + + ProtectedDataGuard guard; + soinfo* si = find_library(ns, translated_name, flags, extinfo, caller); + if (si != nullptr) { + failure_guard.disable(); + si->call_constructors(); + void* handle = si->to_handle(); + LD_LOG(kLogDlopen, + "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p", + si->get_realpath(), si->get_soname(), handle); + return handle; + } + + return nullptr; +} + +int do_dladdr(const void* addr, Dl_info* info) { + // Determine if this address can be found in any library currently mapped. + soinfo* si = find_containing_library(addr); + if (si == nullptr) { + return 0; + } + + memset(info, 0, sizeof(Dl_info)); + + info->dli_fname = si->get_realpath(); + // Address at which the shared object is loaded. + info->dli_fbase = reinterpret_cast<void*>(si->base); + + // Determine if any symbol in the library contains the specified address. + ElfW(Sym)* sym = si->find_symbol_by_address(addr); + if (sym != nullptr) { + info->dli_sname = si->get_string(sym->st_name); + info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym)); + } + + return 1; +} + +static soinfo* soinfo_from_handle(void* handle) { + if ((reinterpret_cast<uintptr_t>(handle) & 1) != 0) { + auto it = g_soinfo_handles_map.find(reinterpret_cast<uintptr_t>(handle)); + if (it == g_soinfo_handles_map.end()) { + return nullptr; + } else { + return it->second; + } + } + + return static_cast<soinfo*>(handle); +} + +bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, + void* caller_addr, void** symbol) { +#if !defined(__LP64__) + if (handle == nullptr) { + DL_ERR("dlsym failed: library handle is null"); + return false; + } +#endif + + if (sym_name == nullptr) { + DL_ERR("dlsym failed: symbol name is null"); + return false; + } + + soinfo* found = nullptr; + const ElfW(Sym)* sym = nullptr; + soinfo* caller = find_containing_library(caller_addr); + android_namespace_t* ns = get_caller_namespace(caller); + + version_info vi_instance; + version_info* vi = nullptr; + + if (sym_ver != nullptr) { + vi_instance.name = sym_ver; + vi_instance.elf_hash = calculate_elf_hash(sym_ver); + vi = &vi_instance; + } + + if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) { + sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle); + } else { + soinfo* si = soinfo_from_handle(handle); + if (si == nullptr) { + DL_ERR("dlsym failed: invalid handle: %p", handle); + return false; + } + sym = dlsym_handle_lookup(si, &found, sym_name, vi); + } + + if (sym != nullptr) { + uint32_t bind = ELF_ST_BIND(sym->st_info); + + if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) { + *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym)); + return true; + } + + DL_ERR("symbol \"%s\" found but not global", symbol_display_name(sym_name, sym_ver).c_str()); + return false; + } + + DL_ERR("undefined symbol: %s", symbol_display_name(sym_name, sym_ver).c_str()); + return false; +} + +int do_dlclose(void* handle) { + ProtectedDataGuard guard; + soinfo* si = soinfo_from_handle(handle); + if (si == nullptr) { + DL_ERR("invalid handle: %p", handle); + return -1; + } + + soinfo_unload(si); + return 0; +} + +bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) { + if (g_public_namespace_initialized) { + DL_ERR("public namespace has already been initialized."); + return false; + } + + if (public_ns_sonames == nullptr || public_ns_sonames[0] == '\0') { + DL_ERR("error initializing public namespace: the list of public libraries is empty."); + return false; + } + + std::vector<std::string> sonames = android::base::Split(public_ns_sonames, ":"); + + ProtectedDataGuard guard; + + auto failure_guard = make_scope_guard([&]() { + g_public_namespace.clear(); + }); + + for (const auto& soname : sonames) { + soinfo* candidate = nullptr; + + find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate); + + if (candidate == nullptr) { + DL_ERR("error initializing public namespace: a library with soname \"%s\"" + " was not found in the default namespace", soname.c_str()); + return false; + } + + candidate->set_nodelete(); + g_public_namespace.push_back(candidate); + } + + g_public_namespace_initialized = true; + + // create anonymous namespace + // When the caller is nullptr - create_namespace will take global group + // from the anonymous namespace, which is fine because anonymous namespace + // is still pointing to the default one. + android_namespace_t* anon_ns = + create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path, + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, &g_default_namespace); + + if (anon_ns == nullptr) { + g_public_namespace_initialized = false; + return false; + } + g_anonymous_namespace = anon_ns; + failure_guard.disable(); + return true; +} + +static void add_soinfos_to_namespace(const soinfo_list_t& soinfos, android_namespace_t* ns) { + ns->add_soinfos(soinfos); + for (auto si : soinfos) { + si->add_secondary_namespace(ns); + } +} + +android_namespace_t* create_namespace(const void* caller_addr, + const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent_namespace) { + if (!g_public_namespace_initialized) { + DL_ERR("cannot create namespace: public namespace is not initialized."); + return nullptr; + } + + if (parent_namespace == nullptr) { + // if parent_namespace is nullptr -> set it to the caller namespace + soinfo* caller_soinfo = find_containing_library(caller_addr); + + parent_namespace = caller_soinfo != nullptr ? + caller_soinfo->get_primary_namespace() : + g_anonymous_namespace; + } + + ProtectedDataGuard guard; + std::vector<std::string> ld_library_paths; + std::vector<std::string> default_library_paths; + std::vector<std::string> permitted_paths; + + parse_path(ld_library_path, ":", &ld_library_paths); + parse_path(default_library_path, ":", &default_library_paths); + parse_path(permitted_when_isolated_path, ":", &permitted_paths); + + android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t(); + ns->set_name(name); + ns->set_isolated((type & ANDROID_NAMESPACE_TYPE_ISOLATED) != 0); + ns->set_ld_library_paths(std::move(ld_library_paths)); + ns->set_default_library_paths(std::move(default_library_paths)); + ns->set_permitted_paths(std::move(permitted_paths)); + + if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) { + // If shared - clone the parent namespace + add_soinfos_to_namespace(parent_namespace->soinfo_list(), ns); + } else { + // If not shared - copy only the shared group + add_soinfos_to_namespace(get_shared_group(parent_namespace), ns); + } + + return ns; +} + +ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { + typedef ElfW(Addr) (*ifunc_resolver_t)(void); + ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr); + ElfW(Addr) ifunc_addr = ifunc_resolver(); + TRACE_TYPE(RELO, "Called ifunc_resolver@%p. The result is %p", + ifunc_resolver, reinterpret_cast<void*>(ifunc_addr)); + + return ifunc_addr; +} + +const version_info* VersionTracker::get_version_info(ElfW(Versym) source_symver) const { + if (source_symver < 2 || + source_symver >= version_infos.size() || + version_infos[source_symver].name == nullptr) { + return nullptr; + } + + return &version_infos[source_symver]; +} + +void VersionTracker::add_version_info(size_t source_index, + ElfW(Word) elf_hash, + const char* ver_name, + const soinfo* target_si) { + if (source_index >= version_infos.size()) { + version_infos.resize(source_index+1); + } + + version_infos[source_index].elf_hash = elf_hash; + version_infos[source_index].name = ver_name; + version_infos[source_index].target_si = target_si; +} + +bool VersionTracker::init_verneed(const soinfo* si_from) { + uintptr_t verneed_ptr = si_from->get_verneed_ptr(); + + if (verneed_ptr == 0) { + return true; + } + + size_t verneed_cnt = si_from->get_verneed_cnt(); + + for (size_t i = 0, offset = 0; i<verneed_cnt; ++i) { + const ElfW(Verneed)* verneed = reinterpret_cast<ElfW(Verneed)*>(verneed_ptr + offset); + size_t vernaux_offset = offset + verneed->vn_aux; + offset += verneed->vn_next; + + if (verneed->vn_version != 1) { + DL_ERR("unsupported verneed[%zd] vn_version: %d (expected 1)", i, verneed->vn_version); + return false; + } + + const char* target_soname = si_from->get_string(verneed->vn_file); + // find it in dependencies + soinfo* target_si = si_from->get_children().find_if([&](const soinfo* si) { + return si->get_soname() != nullptr && strcmp(si->get_soname(), target_soname) == 0; + }); + + if (target_si == nullptr) { + DL_ERR("cannot find \"%s\" from verneed[%zd] in DT_NEEDED list for \"%s\"", + target_soname, i, si_from->get_realpath()); + return false; + } + + for (size_t j = 0; j<verneed->vn_cnt; ++j) { + const ElfW(Vernaux)* vernaux = reinterpret_cast<ElfW(Vernaux)*>(verneed_ptr + vernaux_offset); + vernaux_offset += vernaux->vna_next; + + const ElfW(Word) elf_hash = vernaux->vna_hash; + const char* ver_name = si_from->get_string(vernaux->vna_name); + ElfW(Half) source_index = vernaux->vna_other; + + add_version_info(source_index, elf_hash, ver_name, target_si); + } + } + + return true; +} + +template <typename F> +static bool for_each_verdef(const soinfo* si, F functor) { + if (!si->has_min_version(2)) { + return true; + } + + uintptr_t verdef_ptr = si->get_verdef_ptr(); + if (verdef_ptr == 0) { + return true; + } + + size_t offset = 0; + + size_t verdef_cnt = si->get_verdef_cnt(); + for (size_t i = 0; i<verdef_cnt; ++i) { + const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset); + size_t verdaux_offset = offset + verdef->vd_aux; + offset += verdef->vd_next; + + if (verdef->vd_version != 1) { + DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s", + i, verdef->vd_version, si->get_realpath()); + return false; + } + + if ((verdef->vd_flags & VER_FLG_BASE) != 0) { + // "this is the version of the file itself. It must not be used for + // matching a symbol. It can be used to match references." + // + // http://www.akkadia.org/drepper/symbol-versioning + continue; + } + + if (verdef->vd_cnt == 0) { + DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i); + return false; + } + + const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset); + + if (functor(i, verdef, verdaux) == true) { + break; + } + } + + return true; +} + +bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym) { + if (vi == nullptr) { + *versym = kVersymNotNeeded; + return true; + } + + *versym = kVersymGlobal; + + return for_each_verdef(si, + [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { + if (verdef->vd_hash == vi->elf_hash && + strcmp(vi->name, si->get_string(verdaux->vda_name)) == 0) { + *versym = verdef->vd_ndx; + return true; + } + + return false; + } + ); +} + +bool VersionTracker::init_verdef(const soinfo* si_from) { + return for_each_verdef(si_from, + [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { + add_version_info(verdef->vd_ndx, verdef->vd_hash, + si_from->get_string(verdaux->vda_name), si_from); + return false; + } + ); +} + +bool VersionTracker::init(const soinfo* si_from) { + if (!si_from->has_min_version(2)) { + return true; + } + + return init_verneed(si_from) && init_verdef(si_from); +} + +// TODO (dimitry): Methods below need to be moved out of soinfo +// and in more isolated file in order minimize dependencies on +// unnecessary object in the linker binary. Consider making them +// independent from soinfo (?). +bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, + const char* sym_name, const version_info** vi) { + const ElfW(Versym)* sym_ver_ptr = get_versym(sym); + ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr; + + if (sym_ver != VER_NDX_LOCAL && sym_ver != VER_NDX_GLOBAL) { + *vi = version_tracker.get_version_info(sym_ver); + + if (*vi == nullptr) { + DL_ERR("cannot find verneed/verdef for version index=%d " + "referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_realpath()); + return false; + } + } else { + // there is no version info + *vi = nullptr; + } + + return true; +} + +#if !defined(__mips__) +#if defined(USE_RELA) +static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { + return rela->r_addend; +} +#else +static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { + if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || + ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) { + return *reinterpret_cast<ElfW(Addr)*>(reloc_addr); + } + return 0; +} +#endif + +template<typename ElfRelIteratorT> +bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, + const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { + const auto rel = rel_iterator.next(); + if (rel == nullptr) { + return false; + } + + ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); + ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); + + ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias); + ElfW(Addr) sym_addr = 0; + const char* sym_name = nullptr; + ElfW(Addr) addend = get_addend(rel, reloc); + + DEBUG("Processing \"%s\" relocation at index %zd", get_realpath(), idx); + if (type == R_GENERIC_NONE) { + continue; + } + + const ElfW(Sym)* s = nullptr; + soinfo* lsi = nullptr; + + if (sym != 0) { + sym_name = get_string(symtab_[sym].st_name); + const version_info* vi = nullptr; + + if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) { + return false; + } + + if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) { + return false; + } + + if (s == nullptr) { + // We only allow an undefined symbol if this is a weak reference... + s = &symtab_[sym]; + if (ELF_ST_BIND(s->st_info) != STB_WEAK) { + DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_realpath()); + return false; + } + + /* IHI0044C AAELF 4.5.1.1: + + Libraries are not searched to resolve weak references. + It is not an error for a weak reference to remain unsatisfied. + + During linking, the value of an undefined weak reference is: + - Zero if the relocation type is absolute + - The address of the place if the relocation is pc-relative + - The address of nominal base address if the relocation + type is base-relative. + */ + + switch (type) { + case R_GENERIC_JUMP_SLOT: + case R_GENERIC_GLOB_DAT: + case R_GENERIC_RELATIVE: + case R_GENERIC_IRELATIVE: +#if defined(__aarch64__) + case R_AARCH64_ABS64: + case R_AARCH64_ABS32: + case R_AARCH64_ABS16: +#elif defined(__x86_64__) + case R_X86_64_32: + case R_X86_64_64: +#elif defined(__arm__) + case R_ARM_ABS32: +#elif defined(__i386__) + case R_386_32: +#endif + /* + * The sym_addr was initialized to be zero above, or the relocation + * code below does not care about value of sym_addr. + * No need to do anything. + */ + break; +#if defined(__x86_64__) + case R_X86_64_PC32: + sym_addr = reloc; + break; +#elif defined(__i386__) + case R_386_PC32: + sym_addr = reloc; + break; +#endif + default: + DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx); + return false; + } + } else { // We got a definition. +#if !defined(__LP64__) + // When relocating dso with text_relocation .text segment is + // not executable. We need to restore elf flags before resolving + // STT_GNU_IFUNC symbol. + bool protect_segments = has_text_relocations && + lsi == this && + ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC; + if (protect_segments) { + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + sym_addr = lsi->resolve_symbol_address(s); +#if !defined(__LP64__) + if (protect_segments) { + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + } + count_relocation(kRelocSymbol); + } + + switch (type) { + case R_GENERIC_JUMP_SLOT: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO JMP_SLOT %16p <- %16p %s\n", + reinterpret_cast<void*>(reloc), + reinterpret_cast<void*>(sym_addr + addend), sym_name); + + *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend); + break; + case R_GENERIC_GLOB_DAT: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO GLOB_DAT %16p <- %16p %s\n", + reinterpret_cast<void*>(reloc), + reinterpret_cast<void*>(sym_addr + addend), sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend); + break; + case R_GENERIC_RELATIVE: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n", + reinterpret_cast<void*>(reloc), + reinterpret_cast<void*>(load_bias + addend)); + *reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend); + break; + case R_GENERIC_IRELATIVE: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n", + reinterpret_cast<void*>(reloc), + reinterpret_cast<void*>(load_bias + addend)); + { +#if !defined(__LP64__) + // When relocating dso with text_relocation .text segment is + // not executable. We need to restore elf flags for this + // particular call. + if (has_text_relocations) { + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + ElfW(Addr) ifunc_addr = call_ifunc_resolver(load_bias + addend); +#if !defined(__LP64__) + // Unprotect it afterwards... + if (has_text_relocations) { + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + *reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_addr; + } + break; + +#if defined(__aarch64__) + case R_AARCH64_ABS64: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO ABS64 %16llx <- %16llx %s\n", + reloc, sym_addr + addend, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend; + break; + case R_AARCH64_ABS32: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO ABS32 %16llx <- %16llx %s\n", + reloc, sym_addr + addend, sym_name); + { + const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN); + const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX); + if ((min_value <= (sym_addr + addend)) && + ((sym_addr + addend) <= max_value)) { + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend; + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + sym_addr + addend, min_value, max_value); + return false; + } + } + break; + case R_AARCH64_ABS16: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO ABS16 %16llx <- %16llx %s\n", + reloc, sym_addr + addend, sym_name); + { + const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN); + const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX); + if ((min_value <= (sym_addr + addend)) && + ((sym_addr + addend) <= max_value)) { + *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend); + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + sym_addr + addend, min_value, max_value); + return false; + } + } + break; + case R_AARCH64_PREL64: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL64 %16llx <- %16llx - %16llx %s\n", + reloc, sym_addr + addend, rel->r_offset, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset; + break; + case R_AARCH64_PREL32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL32 %16llx <- %16llx - %16llx %s\n", + reloc, sym_addr + addend, rel->r_offset, sym_name); + { + const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN); + const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX); + if ((min_value <= (sym_addr + addend - rel->r_offset)) && + ((sym_addr + addend - rel->r_offset) <= max_value)) { + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset; + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + sym_addr + addend - rel->r_offset, min_value, max_value); + return false; + } + } + break; + case R_AARCH64_PREL16: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL16 %16llx <- %16llx - %16llx %s\n", + reloc, sym_addr + addend, rel->r_offset, sym_name); + { + const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN); + const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX); + if ((min_value <= (sym_addr + addend - rel->r_offset)) && + ((sym_addr + addend - rel->r_offset) <= max_value)) { + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset; + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + sym_addr + addend - rel->r_offset, min_value, max_value); + return false; + } + } + break; + + case R_AARCH64_COPY: + /* + * ET_EXEC is not supported so this should not happen. + * + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf + * + * Section 4.6.11 "Dynamic relocations" + * R_AARCH64_COPY may only appear in executable objects where e_type is + * set to ET_EXEC. + */ + DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_realpath()); + return false; + case R_AARCH64_TLS_TPREL64: + TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n", + reloc, (sym_addr + addend), rel->r_offset); + break; + case R_AARCH64_TLS_DTPREL32: + TRACE_TYPE(RELO, "RELO TLS_DTPREL32 *** %16llx <- %16llx - %16llx\n", + reloc, (sym_addr + addend), rel->r_offset); + break; +#elif defined(__x86_64__) + case R_X86_64_32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc), + static_cast<size_t>(sym_addr), sym_name); + *reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr + addend; + break; + case R_X86_64_64: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO R_X86_64_64 %08zx <- +%08zx %s", static_cast<size_t>(reloc), + static_cast<size_t>(sym_addr), sym_name); + *reinterpret_cast<Elf64_Addr*>(reloc) = sym_addr + addend; + break; + case R_X86_64_PC32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s", + static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc), + static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name); + *reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr + addend - reloc; + break; +#elif defined(__arm__) + case R_ARM_ABS32: + count_relocation(kRelocAbsolute); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO ABS %08x <- %08x %s", reloc, sym_addr, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr; + break; + case R_ARM_REL32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL32 %08x <- %08x - %08x %s", + reloc, sym_addr, rel->r_offset, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr - rel->r_offset; + break; + case R_ARM_COPY: + /* + * ET_EXEC is not supported so this should not happen. + * + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf + * + * Section 4.6.1.10 "Dynamic relocations" + * R_ARM_COPY may only appear in executable objects where e_type is + * set to ET_EXEC. + */ + DL_ERR("%s R_ARM_COPY relocations are not supported", get_realpath()); + return false; +#elif defined(__i386__) + case R_386_32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO R_386_32 %08x <- +%08x %s", reloc, sym_addr, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr; + break; + case R_386_PC32: + count_relocation(kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO R_386_PC32 %08x <- +%08x (%08x - %08x) %s", + reloc, (sym_addr - reloc), sym_addr, reloc, sym_name); + *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr - reloc); + break; +#endif + default: + DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx); + return false; + } + } + return true; +} +#endif // !defined(__mips__) + +// An empty list of soinfos +static soinfo_list_t g_empty_list; + +bool soinfo::prelink_image() { + /* Extract dynamic section */ + ElfW(Word) dynamic_flags = 0; + phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags); + + /* We can't log anything until the linker is relocated */ + bool relocating_linker = (flags_ & FLAG_LINKER) != 0; + if (!relocating_linker) { + INFO("[ Linking \"%s\" ]", get_realpath()); + DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags_); + } + + if (dynamic == nullptr) { + if (!relocating_linker) { + DL_ERR("missing PT_DYNAMIC in \"%s\"", get_realpath()); + } + return false; + } else { + if (!relocating_linker) { + DEBUG("dynamic = %p", dynamic); + } + } + +#if defined(__arm__) + (void) phdr_table_get_arm_exidx(phdr, phnum, load_bias, + &ARM_exidx, &ARM_exidx_count); +#endif + + // Extract useful information from dynamic section. + // Note that: "Except for the DT_NULL element at the end of the array, + // and the relative order of DT_NEEDED elements, entries may appear in any order." + // + // source: http://www.sco.com/developers/gabi/1998-04-29/ch5.dynamic.html + uint32_t needed_count = 0; + for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { + DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p", + d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val)); + switch (d->d_tag) { + case DT_SONAME: + // this is parsed after we have strtab initialized (see below). + break; + + case DT_HASH: + nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0]; + nchain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; + bucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8); + chain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket_ * 4); + break; + + case DT_GNU_HASH: + gnu_nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0]; + // skip symndx + gnu_maskwords_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2]; + gnu_shift2_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[3]; + + gnu_bloom_filter_ = reinterpret_cast<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16); + gnu_bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_); + // amend chain for symndx = header[1] + gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - + reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; + + if (!powerof2(gnu_maskwords_)) { + DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", + gnu_maskwords_, get_realpath()); + return false; + } + --gnu_maskwords_; + + flags_ |= FLAG_GNU_HASH; + break; + + case DT_STRTAB: + strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr); + break; + + case DT_STRSZ: + strtab_size_ = d->d_un.d_val; + break; + + case DT_SYMTAB: + symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr); + break; + + case DT_SYMENT: + if (d->d_un.d_val != sizeof(ElfW(Sym))) { + DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", + static_cast<size_t>(d->d_un.d_val), get_realpath()); + return false; + } + break; + + case DT_PLTREL: +#if defined(USE_RELA) + if (d->d_un.d_val != DT_RELA) { + DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_RELA", get_realpath()); + return false; + } +#else + if (d->d_un.d_val != DT_REL) { + DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_REL", get_realpath()); + return false; + } +#endif + break; + + case DT_JMPREL: +#if defined(USE_RELA) + plt_rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr); +#else + plt_rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr); +#endif + break; + + case DT_PLTRELSZ: +#if defined(USE_RELA) + plt_rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela)); +#else + plt_rel_count_ = d->d_un.d_val / sizeof(ElfW(Rel)); +#endif + break; + + case DT_PLTGOT: +#if defined(__mips__) + // Used by mips and mips64. + plt_got_ = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr); +#endif + // Ignore for other platforms... (because RTLD_LAZY is not supported) + break; + + case DT_DEBUG: + // Set the DT_DEBUG entry to the address of _r_debug for GDB + // if the dynamic table is writable +// FIXME: not working currently for N64 +// The flags for the LOAD and DYNAMIC program headers do not agree. +// The LOAD section containing the dynamic table has been mapped as +// read-only, but the DYNAMIC header claims it is writable. +#if !(defined(__mips__) && defined(__LP64__)) + if ((dynamic_flags & PF_W) != 0) { + d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug); + } +#endif + break; +#if defined(USE_RELA) + case DT_RELA: + rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr); + break; + + case DT_RELASZ: + rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela)); + break; + + case DT_ANDROID_RELA: + android_relocs_ = reinterpret_cast<uint8_t*>(load_bias + d->d_un.d_ptr); + break; + + case DT_ANDROID_RELASZ: + android_relocs_size_ = d->d_un.d_val; + break; + + case DT_ANDROID_REL: + DL_ERR("unsupported DT_ANDROID_REL in \"%s\"", get_realpath()); + return false; + + case DT_ANDROID_RELSZ: + DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", get_realpath()); + return false; + + case DT_RELAENT: + if (d->d_un.d_val != sizeof(ElfW(Rela))) { + DL_ERR("invalid DT_RELAENT: %zd", static_cast<size_t>(d->d_un.d_val)); + return false; + } + break; + + // ignored (see DT_RELCOUNT comments for details) + case DT_RELACOUNT: + break; + + case DT_REL: + DL_ERR("unsupported DT_REL in \"%s\"", get_realpath()); + return false; + + case DT_RELSZ: + DL_ERR("unsupported DT_RELSZ in \"%s\"", get_realpath()); + return false; + +#else + case DT_REL: + rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr); + break; + + case DT_RELSZ: + rel_count_ = d->d_un.d_val / sizeof(ElfW(Rel)); + break; + + case DT_RELENT: + if (d->d_un.d_val != sizeof(ElfW(Rel))) { + DL_ERR("invalid DT_RELENT: %zd", static_cast<size_t>(d->d_un.d_val)); + return false; + } + break; + + case DT_ANDROID_REL: + android_relocs_ = reinterpret_cast<uint8_t*>(load_bias + d->d_un.d_ptr); + break; + + case DT_ANDROID_RELSZ: + android_relocs_size_ = d->d_un.d_val; + break; + + case DT_ANDROID_RELA: + DL_ERR("unsupported DT_ANDROID_RELA in \"%s\"", get_realpath()); + return false; + + case DT_ANDROID_RELASZ: + DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", get_realpath()); + return false; + + // "Indicates that all RELATIVE relocations have been concatenated together, + // and specifies the RELATIVE relocation count." + // + // TODO: Spec also mentions that this can be used to optimize relocation process; + // Not currently used by bionic linker - ignored. + case DT_RELCOUNT: + break; + + case DT_RELA: + DL_ERR("unsupported DT_RELA in \"%s\"", get_realpath()); + return false; + + case DT_RELASZ: + DL_ERR("unsupported DT_RELASZ in \"%s\"", get_realpath()); + return false; + +#endif + case DT_INIT: + init_func_ = reinterpret_cast<linker_ctor_function_t>(load_bias + d->d_un.d_ptr); + DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_); + break; + + case DT_FINI: + fini_func_ = reinterpret_cast<linker_dtor_function_t>(load_bias + d->d_un.d_ptr); + DEBUG("%s destructors (DT_FINI) found at %p", get_realpath(), fini_func_); + break; + + case DT_INIT_ARRAY: + init_array_ = reinterpret_cast<linker_ctor_function_t*>(load_bias + d->d_un.d_ptr); + DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", get_realpath(), init_array_); + break; + + case DT_INIT_ARRAYSZ: + init_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr)); + break; + + case DT_FINI_ARRAY: + fini_array_ = reinterpret_cast<linker_dtor_function_t*>(load_bias + d->d_un.d_ptr); + DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", get_realpath(), fini_array_); + break; + + case DT_FINI_ARRAYSZ: + fini_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr)); + break; + + case DT_PREINIT_ARRAY: + preinit_array_ = reinterpret_cast<linker_ctor_function_t*>(load_bias + d->d_un.d_ptr); + DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", get_realpath(), preinit_array_); + break; + + case DT_PREINIT_ARRAYSZ: + preinit_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr)); + break; + + case DT_TEXTREL: +#if defined(__LP64__) + DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", get_realpath()); + return false; +#else + has_text_relocations = true; + break; +#endif + + case DT_SYMBOLIC: + has_DT_SYMBOLIC = true; + break; + + case DT_NEEDED: + ++needed_count; + break; + + case DT_FLAGS: + if (d->d_un.d_val & DF_TEXTREL) { +#if defined(__LP64__) + DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", get_realpath()); + return false; +#else + has_text_relocations = true; +#endif + } + if (d->d_un.d_val & DF_SYMBOLIC) { + has_DT_SYMBOLIC = true; + } + break; + + case DT_FLAGS_1: + set_dt_flags_1(d->d_un.d_val); + + if ((d->d_un.d_val & ~SUPPORTED_DT_FLAGS_1) != 0) { + DL_WARN("%s: unsupported flags DT_FLAGS_1=%p", get_realpath(), reinterpret_cast<void*>(d->d_un.d_val)); + } + break; +#if defined(__mips__) + case DT_MIPS_RLD_MAP: + // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB. + { + r_debug** dp = reinterpret_cast<r_debug**>(load_bias + d->d_un.d_ptr); + *dp = &_r_debug; + } + break; + case DT_MIPS_RLD_MAP_REL: + // Set the DT_MIPS_RLD_MAP_REL entry to the address of _r_debug for GDB. + { + r_debug** dp = reinterpret_cast<r_debug**>( + reinterpret_cast<ElfW(Addr)>(d) + d->d_un.d_val); + *dp = &_r_debug; + } + break; + + case DT_MIPS_RLD_VERSION: + case DT_MIPS_FLAGS: + case DT_MIPS_BASE_ADDRESS: + case DT_MIPS_UNREFEXTNO: + break; + + case DT_MIPS_SYMTABNO: + mips_symtabno_ = d->d_un.d_val; + break; + + case DT_MIPS_LOCAL_GOTNO: + mips_local_gotno_ = d->d_un.d_val; + break; + + case DT_MIPS_GOTSYM: + mips_gotsym_ = d->d_un.d_val; + break; +#endif + // Ignored: "Its use has been superseded by the DF_BIND_NOW flag" + case DT_BIND_NOW: + break; + + case DT_VERSYM: + versym_ = reinterpret_cast<ElfW(Versym)*>(load_bias + d->d_un.d_ptr); + break; + + case DT_VERDEF: + verdef_ptr_ = load_bias + d->d_un.d_ptr; + break; + case DT_VERDEFNUM: + verdef_cnt_ = d->d_un.d_val; + break; + + case DT_VERNEED: + verneed_ptr_ = load_bias + d->d_un.d_ptr; + break; + + case DT_VERNEEDNUM: + verneed_cnt_ = d->d_un.d_val; + break; + + case DT_RUNPATH: + // this is parsed after we have strtab initialized (see below). + break; + + default: + if (!relocating_linker) { + DL_WARN("%s: unused DT entry: type %p arg %p", get_realpath(), + reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val)); + } + break; + } + } + +#if defined(__mips__) && !defined(__LP64__) + if (!mips_check_and_adjust_fp_modes()) { + return false; + } +#endif + + DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p", + reinterpret_cast<void*>(base), strtab_, symtab_); + + // Sanity checks. + if (relocating_linker && needed_count != 0) { + DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries"); + return false; + } + if (nbucket_ == 0 && gnu_nbucket_ == 0) { + DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" " + "(new hash type from the future?)", get_realpath()); + return false; + } + if (strtab_ == 0) { + DL_ERR("empty/missing DT_STRTAB in \"%s\"", get_realpath()); + return false; + } + if (symtab_ == 0) { + DL_ERR("empty/missing DT_SYMTAB in \"%s\"", get_realpath()); + return false; + } + + // second pass - parse entries relying on strtab + for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { + switch (d->d_tag) { + case DT_SONAME: + set_soname(get_string(d->d_un.d_val)); + break; + case DT_RUNPATH: + set_dt_runpath(get_string(d->d_un.d_val)); + break; + } + } + + // Before M release linker was using basename in place of soname. + // In the case when dt_soname is absent some apps stop working + // because they can't find dt_needed library by soname. + // This workaround should keep them working. (applies only + // for apps targeting sdk version <=22). Make an exception for + // the main executable and linker; they do not need to have dt_soname + if (soname_ == nullptr && this != somain && (flags_ & FLAG_LINKER) == 0 && + get_application_target_sdk_version() <= 22) { + soname_ = basename(realpath_.c_str()); + DL_WARN("%s: is missing DT_SONAME will use basename as a replacement: \"%s\"", + get_realpath(), soname_); + // Don't call add_dlwarning because a missing DT_SONAME isn't important enough to show in the UI + } + return true; +} + +bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, + const android_dlextinfo* extinfo) { + + local_group_root_ = local_group.front(); + if (local_group_root_ == nullptr) { + local_group_root_ = this; + } + + if ((flags_ & FLAG_LINKER) == 0 && local_group_root_ == this) { + target_sdk_version_ = get_application_target_sdk_version(); + } + + VersionTracker version_tracker; + + if (!version_tracker.init(this)) { + return false; + } + +#if !defined(__LP64__) + if (has_text_relocations) { + // Fail if app is targeting sdk version > 22 + if (get_application_target_sdk_version() > 22) { + PRINT("%s: has text relocations", get_realpath()); + DL_ERR("%s: has text relocations", get_realpath()); + return false; + } + // Make segments writable to allow text relocations to work properly. We will later call + // phdr_table_protect_segments() after all of them are applied. + DL_WARN("%s has text relocations. This is wasting memory and prevents " + "security hardening. Please fix.", get_realpath()); + add_dlwarning(get_realpath(), "text relocations"); + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + + if (android_relocs_ != nullptr) { + // check signature + if (android_relocs_size_ > 3 && + android_relocs_[0] == 'A' && + android_relocs_[1] == 'P' && + android_relocs_[2] == 'S' && + android_relocs_[3] == '2') { + DEBUG("[ android relocating %s ]", get_realpath()); + + bool relocated = false; + const uint8_t* packed_relocs = android_relocs_ + 4; + const size_t packed_relocs_size = android_relocs_size_ - 4; + + relocated = relocate( + version_tracker, + packed_reloc_iterator<sleb128_decoder>( + sleb128_decoder(packed_relocs, packed_relocs_size)), + global_group, local_group); + + if (!relocated) { + return false; + } + } else { + DL_ERR("bad android relocation header."); + return false; + } + } + +#if defined(USE_RELA) + if (rela_ != nullptr) { + DEBUG("[ relocating %s ]", get_realpath()); + if (!relocate(version_tracker, + plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) { + return false; + } + } + if (plt_rela_ != nullptr) { + DEBUG("[ relocating %s plt ]", get_realpath()); + if (!relocate(version_tracker, + plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) { + return false; + } + } +#else + if (rel_ != nullptr) { + DEBUG("[ relocating %s ]", get_realpath()); + if (!relocate(version_tracker, + plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) { + return false; + } + } + if (plt_rel_ != nullptr) { + DEBUG("[ relocating %s plt ]", get_realpath()); + if (!relocate(version_tracker, + plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) { + return false; + } + } +#endif + +#if defined(__mips__) + if (!mips_relocate_got(version_tracker, global_group, local_group)) { + return false; + } +#endif + + DEBUG("[ finished linking %s ]", get_realpath()); + +#if !defined(__LP64__) + if (has_text_relocations) { + // All relocations are done, we can protect our segments back to read-only. + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + + // We can also turn on GNU RELRO protection if we're not linking the dynamic linker + // itself --- it can't make system calls yet, and will have to call protect_relro later. + if (!is_linker() && !protect_relro()) { + return false; + } + + /* Handle serializing/sharing the RELRO segment */ + if (extinfo && (extinfo->flags & ANDROID_DLEXT_WRITE_RELRO)) { + if (phdr_table_serialize_gnu_relro(phdr, phnum, load_bias, + extinfo->relro_fd) < 0) { + DL_ERR("failed serializing GNU RELRO section for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } else if (extinfo && (extinfo->flags & ANDROID_DLEXT_USE_RELRO)) { + if (phdr_table_map_gnu_relro(phdr, phnum, load_bias, + extinfo->relro_fd) < 0) { + DL_ERR("failed mapping GNU RELRO section for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } + + notify_gdb_of_load(this); + return true; +} + +bool soinfo::protect_relro() { + if (phdr_table_protect_gnu_relro(phdr, phnum, load_bias) < 0) { + DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + return true; +} + +/* + * This function add vdso to internal dso list. + * It helps to stack unwinding through signal handlers. + * Also, it makes bionic more like glibc. + */ +static void add_vdso(KernelArgumentBlock& args __unused) { +#if defined(AT_SYSINFO_EHDR) + ElfW(Ehdr)* ehdr_vdso = reinterpret_cast<ElfW(Ehdr)*>(args.getauxval(AT_SYSINFO_EHDR)); + if (ehdr_vdso == nullptr) { + return; + } + + soinfo* si = soinfo_alloc(&g_default_namespace, "[vdso]", nullptr, 0, 0); + + si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff); + si->phnum = ehdr_vdso->e_phnum; + si->base = reinterpret_cast<ElfW(Addr)>(ehdr_vdso); + si->size = phdr_table_get_load_size(si->phdr, si->phnum); + si->load_bias = get_elf_exec_load_bias(ehdr_vdso); + + si->prelink_image(); + si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr); +#endif +} + +/* gdb expects the linker to be in the debug shared object list. + * Without this, gdb has trouble locating the linker's ".text" + * and ".plt" sections. Gdb could also potentially use this to + * relocate the offset of our exported 'rtld_db_dlactivity' symbol. + * Note that the linker shouldn't be on the soinfo list. + */ +static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { + static link_map linker_link_map_for_gdb; +#if defined(__LP64__) + static char kLinkerPath[] = "/system/bin/linker64"; +#else + static char kLinkerPath[] = "/system/bin/linker"; +#endif + + linker_link_map_for_gdb.l_addr = linker_base; + linker_link_map_for_gdb.l_name = kLinkerPath; + + /* + * Set the dynamic field in the link map otherwise gdb will complain with + * the following: + * warning: .dynamic section for "/system/bin/linker" is not at the + * expected address (wrong library or version mismatch?) + */ + ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_base); + ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_base + elf_hdr->e_phoff); + phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, + &linker_link_map_for_gdb.l_ld, nullptr); + + insert_link_map_into_debug_map(&linker_link_map_for_gdb); +} + +static void init_default_namespace() { + g_default_namespace.set_name("(default)"); + g_default_namespace.set_isolated(false); + + const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum, + somain->load_bias); + const char* bname = basename(interp); + if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0)) { + g_default_ld_paths = kAsanDefaultLdPaths; + g_is_asan = true; + } else { + g_default_ld_paths = kDefaultLdPaths; + } + + std::vector<std::string> ld_default_paths; + for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) { + ld_default_paths.push_back(g_default_ld_paths[i]); + } + + g_default_namespace.set_default_library_paths(std::move(ld_default_paths)); +}; + +extern "C" int __system_properties_init(void); + +static const char* get_executable_path() { + static std::string executable_path; + if (executable_path.empty()) { + char path[PATH_MAX]; + ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path)); + if (path_len == -1 || path_len >= static_cast<ssize_t>(sizeof(path))) { + __libc_fatal("readlink('/proc/self/exe') failed: %s", strerror(errno)); + } + executable_path = std::string(path, path_len); + } + + return executable_path.c_str(); +} + +/* + * This code is called after the linker has linked itself and + * fixed it's own GOT. It is safe to make references to externs + * and other non-local data at this point. + */ +static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) { +#if TIMING + struct timeval t0, t1; + gettimeofday(&t0, 0); +#endif + + // Sanitize the environment. + __libc_init_AT_SECURE(args); + + // Initialize system properties + __system_properties_init(); // may use 'environ' + + // Register the debuggerd signal handler. + debuggerd_callbacks_t callbacks = { + .get_abort_message = []() { + return g_abort_message; + }, + .post_dump = ¬ify_gdb_of_libraries, + }; + debuggerd_init(&callbacks); + + g_linker_logger.ResetState(); + + // Get a few environment variables. + const char* LD_DEBUG = getenv("LD_DEBUG"); + if (LD_DEBUG != nullptr) { + g_ld_debug_verbosity = atoi(LD_DEBUG); + } + +#if defined(__LP64__) + INFO("[ Android dynamic linker (64-bit) ]"); +#else + INFO("[ Android dynamic linker (32-bit) ]"); +#endif + + // These should have been sanitized by __libc_init_AT_SECURE, but the test + // doesn't cost us anything. + const char* ldpath_env = nullptr; + const char* ldpreload_env = nullptr; + if (!getauxval(AT_SECURE)) { + ldpath_env = getenv("LD_LIBRARY_PATH"); + if (ldpath_env != nullptr) { + INFO("[ LD_LIBRARY_PATH set to \"%s\" ]", ldpath_env); + } + ldpreload_env = getenv("LD_PRELOAD"); + if (ldpreload_env != nullptr) { + INFO("[ LD_PRELOAD set to \"%s\" ]", ldpreload_env); + } + } + + const char* executable_path = get_executable_path(); + struct stat file_stat; + if (TEMP_FAILURE_RETRY(stat(executable_path, &file_stat)) != 0) { + __libc_fatal("unable to stat file for the executable \"%s\": %s", executable_path, strerror(errno)); + } + + soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL); + if (si == nullptr) { + __libc_fatal("Couldn't allocate soinfo: out of memory?"); + } + + /* bootstrap the link map, the main exe always needs to be first */ + si->set_main_executable(); + link_map* map = &(si->link_map_head); + + // Register the main executable and the linker upfront to have + // gdb aware of them before loading the rest of the dependency + // tree. + map->l_addr = 0; + map->l_name = const_cast<char*>(executable_path); + insert_link_map_into_debug_map(map); + init_linker_info_for_gdb(linker_base); + + // Extract information passed from the kernel. + si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR)); + si->phnum = args.getauxval(AT_PHNUM); + + /* Compute the value of si->base. We can't rely on the fact that + * the first entry is the PHDR because this will not be true + * for certain executables (e.g. some in the NDK unit test suite) + */ + si->base = 0; + si->size = phdr_table_get_load_size(si->phdr, si->phnum); + si->load_bias = 0; + for (size_t i = 0; i < si->phnum; ++i) { + if (si->phdr[i].p_type == PT_PHDR) { + si->load_bias = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_vaddr; + si->base = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_offset; + break; + } + } + si->dynamic = nullptr; + + ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base); + if (elf_hdr->e_type != ET_DYN) { + __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.", + g_argv[0]); + } + + // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid). + parse_LD_LIBRARY_PATH(ldpath_env); + parse_LD_PRELOAD(ldpreload_env); + + somain = si; + + init_default_namespace(); + + if (!si->prelink_image()) { + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); + } + + // add somain to global group + si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); + + // Load ld_preloads and dependencies. + StringLinkedList needed_library_name_list; + size_t needed_libraries_count = 0; + size_t ld_preloads_count = 0; + + for (const auto& ld_preload_name : g_ld_preload_names) { + needed_library_name_list.push_back(ld_preload_name.c_str()); + ++needed_libraries_count; + ++ld_preloads_count; + } + + for_each_dt_needed(si, [&](const char* name) { + needed_library_name_list.push_back(name); + ++needed_libraries_count; + }); + + const char* needed_library_names[needed_libraries_count]; + + memset(needed_library_names, 0, sizeof(needed_library_names)); + needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count); + + if (needed_libraries_count > 0 && + !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count, + nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr, + /* add_as_children */ true)) { + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); + } else if (needed_libraries_count == 0) { + if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) { + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); + } + si->increment_ref_count(); + } + + add_vdso(args); + + { + ProtectedDataGuard guard; + + si->call_pre_init_constructors(); + + /* After the prelink_image, the si->load_bias is initialized. + * For so lib, the map->l_addr will be updated in notify_gdb_of_load. + * We need to update this value for so exe here. So Unwind_Backtrace + * for some arch like x86 could work correctly within so exe. + */ + map->l_addr = si->load_bias; + si->call_constructors(); + } + +#if TIMING + gettimeofday(&t1, nullptr); + PRINT("LINKER TIME: %s: %d microseconds", g_argv[0], (int) ( + (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) - + (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec))); +#endif +#if STATS + PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv[0], + linker_stats.count[kRelocAbsolute], + linker_stats.count[kRelocRelative], + linker_stats.count[kRelocCopy], + linker_stats.count[kRelocSymbol]); +#endif +#if COUNT_PAGES + { + unsigned n; + unsigned i; + unsigned count = 0; + for (n = 0; n < 4096; n++) { + if (bitmask[n]) { + unsigned x = bitmask[n]; +#if defined(__LP64__) + for (i = 0; i < 32; i++) { +#else + for (i = 0; i < 8; i++) { +#endif + if (x & 1) { + count++; + } + x >>= 1; + } + } + } + PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv[0], count, count * 4); + } +#endif + +#if TIMING || STATS || COUNT_PAGES + fflush(stdout); +#endif + + ElfW(Addr) entry = args.getauxval(AT_ENTRY); + TRACE("[ Ready to execute \"%s\" @ %p ]", si->get_realpath(), reinterpret_cast<void*>(entry)); + return entry; +} + +/* Compute the load-bias of an existing executable. This shall only + * be used to compute the load bias of an executable or shared library + * that was loaded by the kernel itself. + * + * Input: + * elf -> address of ELF header, assumed to be at the start of the file. + * Return: + * load bias, i.e. add the value of any p_vaddr in the file to get + * the corresponding address in memory. + */ +static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) { + ElfW(Addr) offset = elf->e_phoff; + const ElfW(Phdr)* phdr_table = + reinterpret_cast<const ElfW(Phdr)*>(reinterpret_cast<uintptr_t>(elf) + offset); + const ElfW(Phdr)* phdr_end = phdr_table + elf->e_phnum; + + for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) { + if (phdr->p_type == PT_LOAD) { + return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr; + } + } + return 0; +} + +static void __linker_cannot_link(const char* argv0) { + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", argv0, linker_get_error_buffer()); +} + +/* + * This is the entry point for the linker, called from begin.S. This + * method is responsible for fixing the linker's own relocations, and + * then calling __linker_init_post_relocation(). + * + * Because this method is called before the linker has fixed it's own + * relocations, any attempt to reference an extern variable, extern + * function, or other GOT reference will generate a segfault. + */ +extern "C" ElfW(Addr) __linker_init(void* raw_args) { + KernelArgumentBlock args(raw_args); + + ElfW(Addr) linker_addr = args.getauxval(AT_BASE); + ElfW(Addr) entry_point = args.getauxval(AT_ENTRY); + 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(nullptr, nullptr, nullptr, 0, 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 + // already linked by PT_INTERP. + // + // This happens when user tries to run 'adb shell /system/bin/linker' + // see also https://code.google.com/p/android/issues/detail?id=63174 + if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) { + __libc_format_fd(STDOUT_FILENO, + "This is %s, the helper program for shared library executables.\n", + args.argv[0]); + exit(0); + } + + linker_so.base = linker_addr; + linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum); + linker_so.load_bias = get_elf_exec_load_bias(elf_hdr); + linker_so.dynamic = nullptr; + linker_so.phdr = phdr; + linker_so.phnum = elf_hdr->e_phnum; + linker_so.set_linker_flag(); + + // Prelink the linker so we can access linker globals. + if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]); + + // This might not be obvious... The reasons why we pass g_empty_list + // in place of local_group here are (1) we do not really need it, because + // linker is built with DT_SYMBOLIC and therefore relocates its symbols against + // itself without having to look into local_group and (2) allocators + // are not yet initialized, and therefore we cannot use linked_list.push_* + // functions at this point. + if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]); + +#if defined(__i386__) + // On x86, we can't make system calls before this point. + // We can't move this up because this needs to assign to a global. + // Note that until we call __libc_init_main_thread below we have + // no TLS, so you shouldn't make a system call that can fail, because + // it will SEGV when it tries to set errno. + __libc_init_sysinfo(args); +#endif + + // Initialize the main thread (including TLS, so system calls really work). + __libc_init_main_thread(args); + + // We didn't protect the linker's RELRO pages in link_image because we + // couldn't make system calls on x86 at that point, but we can now... + if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]); + + // Initialize the linker's static libc's globals + __libc_init_globals(args); + + // store argc/argv/envp to use them for calling constructors + g_argc = args.argc; + g_argv = args.argv; + g_envp = args.envp; + + // Initialize the linker's own global variables + linker_so.call_constructors(); + + // Initialize static variables. Note that in order to + // get correct libdl_info we need to call constructors + // before get_libdl_info(). + solist = get_libdl_info(); + sonext = get_libdl_info(); + g_default_namespace.add_soinfo(get_libdl_info()); + + // We have successfully fixed our own relocations. It's safe to run + // the main part of the linker now. + args.abort_message_ptr = &g_abort_message; + ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr); + + INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address)); + + // Return the address that the calling assembly stub should jump to. + return start_address; +}
diff --git a/linker/linker.h b/linker/linker.h new file mode 100644 index 0000000..4787471 --- /dev/null +++ b/linker/linker.h
@@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_H_ +#define _LINKER_H_ + +#include <dlfcn.h> +#include <android/dlext.h> +#include <elf.h> +#include <inttypes.h> +#include <link.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "private/bionic_page.h" +#include "private/libc_logging.h" +#include "linked_list.h" +#include "linker_common_types.h" +#include "linker_logger.h" +#include "linker_soinfo.h" + +#include <string> +#include <vector> + +#if defined(__LP64__) +#define ELFW(what) ELF64_ ## what +#else +#define ELFW(what) ELF32_ ## what +#endif + +// mips64 interprets Elf64_Rel structures' r_info field differently. +// bionic (like other C libraries) has macros that assume regular ELF files, +// but the dynamic linker needs to be able to load mips64 ELF files. +#if defined(__mips__) && defined(__LP64__) +#undef ELF64_R_SYM +#undef ELF64_R_TYPE +#undef ELF64_R_INFO +#define ELF64_R_SYM(info) (((info) >> 0) & 0xffffffff) +#define ELF64_R_SSYM(info) (((info) >> 32) & 0xff) +#define ELF64_R_TYPE3(info) (((info) >> 40) & 0xff) +#define ELF64_R_TYPE2(info) (((info) >> 48) & 0xff) +#define ELF64_R_TYPE(info) (((info) >> 56) & 0xff) +#endif + +#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE | DF_1_PIE) + +// Class used construct version dependency graph. +class VersionTracker { + public: + VersionTracker() = default; + bool init(const soinfo* si_from); + + const version_info* get_version_info(ElfW(Versym) source_symver) const; + private: + bool init_verneed(const soinfo* si_from); + bool init_verdef(const soinfo* si_from); + void add_version_info(size_t source_index, ElfW(Word) elf_hash, + const char* ver_name, const soinfo* target_si); + + std::vector<version_info> version_infos; + + DISALLOW_COPY_AND_ASSIGN(VersionTracker); +}; + +bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, + soinfo** si_found_in, const soinfo_list_t& global_group, + const soinfo_list_t& local_group, const ElfW(Sym)** symbol); + +enum RelocationKind { + kRelocAbsolute = 0, + kRelocRelative, + kRelocCopy, + kRelocSymbol, + kRelocMax +}; + +void count_relocation(RelocationKind kind); + +soinfo* get_libdl_info(); + +void do_android_get_LD_LIBRARY_PATH(char*, size_t); +void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); +void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr); +int do_dlclose(void* handle); + +int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data); + +bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, + void* caller_addr, void** symbol); + +int do_dladdr(const void* addr, Dl_info* info); + +void set_application_target_sdk_version(uint32_t target); +uint32_t get_application_target_sdk_version(); + +enum { + /* A regular namespace is the namespace with a custom search path that does + * not impose any restrictions on the location of native libraries. + */ + ANDROID_NAMESPACE_TYPE_REGULAR = 0, + + /* An isolated namespace requires all the libraries to be on the search path + * or under permitted_when_isolated_path. The search path is the union of + * ld_library_path and default_library_path. + */ + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + + /* The shared namespace clones the list of libraries of the caller namespace upon creation + * which means that they are shared between namespaces - the caller namespace and the new one + * will use the same copy of a library if it was loaded prior to android_create_namespace call. + * + * Note that libraries loaded after the namespace is created will not be shared. + * + * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor + * permitted_path from the caller's namespace. + */ + ANDROID_NAMESPACE_TYPE_SHARED = 2, + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, +}; + +bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path); +android_namespace_t* create_namespace(const void* caller_addr, + const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent_namespace); + +constexpr unsigned kLibraryAlignmentBits = 18; +constexpr size_t kLibraryAlignment = 1UL << kLibraryAlignmentBits; + +#endif
diff --git a/linker/linker_allocator.cpp b/linker/linker_allocator.cpp new file mode 100644 index 0000000..dc6dfc1 --- /dev/null +++ b/linker/linker_allocator.cpp
@@ -0,0 +1,345 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_allocator.h" +#include "linker_debug.h" +#include "linker.h" + +#include <algorithm> +#include <vector> + +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "private/bionic_prctl.h" + +// +// LinkerMemeoryAllocator is general purpose allocator +// designed to provide the same functionality as the malloc/free/realloc +// libc functions. +// +// On alloc: +// If size is >= 1k allocator proxies malloc call directly to mmap +// If size < 1k allocator uses SmallObjectAllocator for the size +// rounded up to the nearest power of two. +// +// On free: +// +// For a pointer allocated using proxy-to-mmap allocator unmaps +// the memory. +// +// For a pointer allocated using SmallObjectAllocator it adds +// the block to free_blocks_list_. If the number of free pages reaches 2, +// SmallObjectAllocator munmaps one of the pages keeping the other one +// in reserve. + +static const char kSignature[4] = {'L', 'M', 'A', 1}; + +static const size_t kSmallObjectMaxSize = 1 << kSmallObjectMaxSizeLog2; + +// This type is used for large allocations (with size >1k) +static const uint32_t kLargeObject = 111; + +bool operator<(const small_object_page_record& one, const small_object_page_record& two) { + return one.page_addr < two.page_addr; +} + +static inline uint16_t log2(size_t number) { + uint16_t result = 0; + number--; + + while (number != 0) { + result++; + number >>= 1; + } + + return result; +} + +LinkerSmallObjectAllocator::LinkerSmallObjectAllocator(uint32_t type, size_t block_size) + : type_(type), block_size_(block_size), free_pages_cnt_(0), free_blocks_list_(nullptr) {} + +void* LinkerSmallObjectAllocator::alloc() { + CHECK(block_size_ != 0); + + if (free_blocks_list_ == nullptr) { + alloc_page(); + } + + small_object_block_record* block_record = free_blocks_list_; + if (block_record->free_blocks_cnt > 1) { + small_object_block_record* next_free = reinterpret_cast<small_object_block_record*>( + reinterpret_cast<uint8_t*>(block_record) + block_size_); + next_free->next = block_record->next; + next_free->free_blocks_cnt = block_record->free_blocks_cnt - 1; + free_blocks_list_ = next_free; + } else { + free_blocks_list_ = block_record->next; + } + + // bookkeeping... + auto page_record = find_page_record(block_record); + + if (page_record->allocated_blocks_cnt == 0) { + free_pages_cnt_--; + } + + page_record->free_blocks_cnt--; + page_record->allocated_blocks_cnt++; + + memset(block_record, 0, block_size_); + + return block_record; +} + +void LinkerSmallObjectAllocator::free_page(linker_vector_t::iterator page_record) { + void* page_start = reinterpret_cast<void*>(page_record->page_addr); + void* page_end = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(page_start) + PAGE_SIZE); + + while (free_blocks_list_ != nullptr && + free_blocks_list_ > page_start && + free_blocks_list_ < page_end) { + free_blocks_list_ = free_blocks_list_->next; + } + + small_object_block_record* current = free_blocks_list_; + + while (current != nullptr) { + while (current->next > page_start && current->next < page_end) { + current->next = current->next->next; + } + + current = current->next; + } + + munmap(page_start, PAGE_SIZE); + page_records_.erase(page_record); + free_pages_cnt_--; +} + +void LinkerSmallObjectAllocator::free(void* ptr) { + auto page_record = find_page_record(ptr); + + ssize_t offset = reinterpret_cast<uintptr_t>(ptr) - sizeof(page_info); + + if (offset % block_size_ != 0) { + __libc_fatal("invalid pointer: %p (block_size=%zd)", ptr, block_size_); + } + + memset(ptr, 0, block_size_); + small_object_block_record* block_record = reinterpret_cast<small_object_block_record*>(ptr); + + block_record->next = free_blocks_list_; + block_record->free_blocks_cnt = 1; + + free_blocks_list_ = block_record; + + page_record->free_blocks_cnt++; + page_record->allocated_blocks_cnt--; + + if (page_record->allocated_blocks_cnt == 0) { + if (free_pages_cnt_++ > 1) { + // if we already have a free page - unmap this one. + free_page(page_record); + } + } +} + +linker_vector_t::iterator LinkerSmallObjectAllocator::find_page_record(void* ptr) { + void* addr = reinterpret_cast<void*>(PAGE_START(reinterpret_cast<uintptr_t>(ptr))); + small_object_page_record boundary; + boundary.page_addr = addr; + linker_vector_t::iterator it = std::lower_bound( + page_records_.begin(), page_records_.end(), boundary); + + if (it == page_records_.end() || it->page_addr != addr) { + // not found... + __libc_fatal("page record for %p was not found (block_size=%zd)", ptr, block_size_); + } + + return it; +} + +void LinkerSmallObjectAllocator::create_page_record(void* page_addr, size_t free_blocks_cnt) { + small_object_page_record record; + record.page_addr = page_addr; + record.free_blocks_cnt = free_blocks_cnt; + record.allocated_blocks_cnt = 0; + + linker_vector_t::iterator it = std::lower_bound( + page_records_.begin(), page_records_.end(), record); + page_records_.insert(it, record); +} + +void LinkerSmallObjectAllocator::alloc_page() { + static_assert(sizeof(page_info) % 16 == 0, + "sizeof(page_info) is not multiple of 16"); + void* map_ptr = mmap(nullptr, PAGE_SIZE, + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + if (map_ptr == MAP_FAILED) { + __libc_fatal("mmap failed"); + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, PAGE_SIZE, "linker_alloc_small_objects"); + + memset(map_ptr, 0, PAGE_SIZE); + + page_info* info = reinterpret_cast<page_info*>(map_ptr); + memcpy(info->signature, kSignature, sizeof(kSignature)); + info->type = type_; + info->allocator_addr = this; + + size_t free_blocks_cnt = (PAGE_SIZE - sizeof(page_info))/block_size_; + + create_page_record(map_ptr, free_blocks_cnt); + + small_object_block_record* first_block = reinterpret_cast<small_object_block_record*>(info + 1); + + first_block->next = free_blocks_list_; + first_block->free_blocks_cnt = free_blocks_cnt; + + free_blocks_list_ = first_block; +} + + +void LinkerMemoryAllocator::initialize_allocators() { + if (allocators_ != nullptr) { + return; + } + + LinkerSmallObjectAllocator* allocators = + reinterpret_cast<LinkerSmallObjectAllocator*>(allocators_buf_); + + for (size_t i = 0; i < kSmallObjectAllocatorsCount; ++i) { + uint32_t type = i + kSmallObjectMinSizeLog2; + new (allocators + i) LinkerSmallObjectAllocator(type, 1 << type); + } + + allocators_ = allocators; +} + +void* LinkerMemoryAllocator::alloc_mmap(size_t size) { + size_t allocated_size = PAGE_END(size + sizeof(page_info)); + void* map_ptr = mmap(nullptr, allocated_size, + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + + if (map_ptr == MAP_FAILED) { + __libc_fatal("mmap failed"); + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "linker_alloc_lob"); + + memset(map_ptr, 0, allocated_size); + + page_info* info = reinterpret_cast<page_info*>(map_ptr); + memcpy(info->signature, kSignature, sizeof(kSignature)); + info->type = kLargeObject; + info->allocated_size = allocated_size; + + return info + 1; +} + +void* LinkerMemoryAllocator::alloc(size_t size) { + // treat alloc(0) as alloc(1) + if (size == 0) { + size = 1; + } + + if (size > kSmallObjectMaxSize) { + return alloc_mmap(size); + } + + uint16_t log2_size = log2(size); + + if (log2_size < kSmallObjectMinSizeLog2) { + log2_size = kSmallObjectMinSizeLog2; + } + + return get_small_object_allocator(log2_size)->alloc(); +} + +page_info* LinkerMemoryAllocator::get_page_info(void* ptr) { + page_info* info = reinterpret_cast<page_info*>(PAGE_START(reinterpret_cast<size_t>(ptr))); + if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) { + __libc_fatal("invalid pointer %p (page signature mismatch)", ptr); + } + + return info; +} + +void* LinkerMemoryAllocator::realloc(void* ptr, size_t size) { + if (ptr == nullptr) { + return alloc(size); + } + + if (size == 0) { + free(ptr); + return nullptr; + } + + page_info* info = get_page_info(ptr); + + size_t old_size = 0; + + if (info->type == kLargeObject) { + old_size = info->allocated_size - sizeof(page_info); + } else { + LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type); + if (allocator != info->allocator_addr) { + __libc_fatal("invalid pointer %p (page signature mismatch)", ptr); + } + + old_size = allocator->get_block_size(); + } + + if (old_size < size) { + void *result = alloc(size); + memcpy(result, ptr, old_size); + free(ptr); + return result; + } + + return ptr; +} + +void LinkerMemoryAllocator::free(void* ptr) { + if (ptr == nullptr) { + return; + } + + page_info* info = get_page_info(ptr); + + if (info->type == kLargeObject) { + munmap(info, info->allocated_size); + } else { + LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type); + if (allocator != info->allocator_addr) { + __libc_fatal("invalid pointer %p (invalid allocator address for the page)", ptr); + } + + allocator->free(ptr); + } +} + +LinkerSmallObjectAllocator* LinkerMemoryAllocator::get_small_object_allocator(uint32_t type) { + if (type < kSmallObjectMinSizeLog2 || type > kSmallObjectMaxSizeLog2) { + __libc_fatal("invalid type: %u", type); + } + + initialize_allocators(); + return &allocators_[type - kSmallObjectMinSizeLog2]; +}
diff --git a/linker/linker_allocator.h b/linker/linker_allocator.h new file mode 100644 index 0000000..8f90dbf --- /dev/null +++ b/linker/linker_allocator.h
@@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKER_ALLOCATOR_H +#define __LINKER_ALLOCATOR_H + +#include <stdlib.h> +#include <sys/cdefs.h> +#include <sys/mman.h> +#include <stddef.h> +#include <unistd.h> + +#include <vector> + +#include "private/bionic_prctl.h" +#include "private/libc_logging.h" + +const uint32_t kSmallObjectMaxSizeLog2 = 10; +const uint32_t kSmallObjectMinSizeLog2 = 4; +const uint32_t kSmallObjectAllocatorsCount = kSmallObjectMaxSizeLog2 - kSmallObjectMinSizeLog2 + 1; + +class LinkerSmallObjectAllocator; + +// This structure is placed at the beginning of each addressable page +// and has all information we need to find the corresponding memory allocator. +struct page_info { + char signature[4]; + uint32_t type; + union { + // we use allocated_size for large objects allocator + size_t allocated_size; + // and allocator_addr for small ones. + LinkerSmallObjectAllocator* allocator_addr; + }; +} __attribute__((aligned(16))); + +struct small_object_page_record { + void* page_addr; + size_t free_blocks_cnt; + size_t allocated_blocks_cnt; +}; + +// for lower_bound... +bool operator<(const small_object_page_record& one, const small_object_page_record& two); + +struct small_object_block_record { + small_object_block_record* next; + size_t free_blocks_cnt; +}; + +// This is implementation for std::vector allocator +template <typename T> +class linker_vector_allocator { + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + T* allocate(size_t n, const T* hint = nullptr) { + size_t size = n * sizeof(T); + void* ptr = mmap(const_cast<T*>(hint), size, + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + if (ptr == MAP_FAILED) { + // Spec says we need to throw std::bad_alloc here but because our + // code does not support exception handling anyways - we are going to abort. + __libc_fatal("mmap failed"); + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, "linker_alloc_vector"); + + return reinterpret_cast<T*>(ptr); + } + + void deallocate(T* ptr, size_t n) { + munmap(ptr, n * sizeof(T)); + } +}; + +typedef + std::vector<small_object_page_record, linker_vector_allocator<small_object_page_record>> + linker_vector_t; + + +class LinkerSmallObjectAllocator { + public: + LinkerSmallObjectAllocator(uint32_t type, size_t block_size); + void* alloc(); + void free(void* ptr); + + size_t get_block_size() const { return block_size_; } + private: + void alloc_page(); + void free_page(linker_vector_t::iterator page_record); + linker_vector_t::iterator find_page_record(void* ptr); + void create_page_record(void* page_addr, size_t free_blocks_cnt); + + uint32_t type_; + size_t block_size_; + + size_t free_pages_cnt_; + small_object_block_record* free_blocks_list_; + + // sorted vector of page records + linker_vector_t page_records_; +}; + +class LinkerMemoryAllocator { + public: + constexpr LinkerMemoryAllocator() : allocators_(nullptr), allocators_buf_() {} + void* alloc(size_t size); + + // Note that this implementation of realloc never shrinks allocation + void* realloc(void* ptr, size_t size); + void free(void* ptr); + private: + void* alloc_mmap(size_t size); + page_info* get_page_info(void* ptr); + LinkerSmallObjectAllocator* get_small_object_allocator(uint32_t type); + void initialize_allocators(); + + LinkerSmallObjectAllocator* allocators_; + uint8_t allocators_buf_[sizeof(LinkerSmallObjectAllocator)*kSmallObjectAllocatorsCount]; +}; + + +#endif /* __LINKER_ALLOCATOR_H */
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp new file mode 100644 index 0000000..23298a4 --- /dev/null +++ b/linker/linker_block_allocator.cpp
@@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_block_allocator.h" +#include <inttypes.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "private/bionic_prctl.h" + +// the multiplier should be power of 2 +static constexpr size_t round_up(size_t size, size_t multiplier) { + return (size + (multiplier - 1)) & ~(multiplier-1); +} + +struct LinkerBlockAllocatorPage { + LinkerBlockAllocatorPage* next; + uint8_t bytes[PAGE_SIZE - 16] __attribute__((aligned(16))); +}; + +struct FreeBlockInfo { + void* next_block; + size_t num_free_blocks; +}; + +LinkerBlockAllocator::LinkerBlockAllocator(size_t block_size) + : block_size_( + round_up(block_size < sizeof(FreeBlockInfo) ? sizeof(FreeBlockInfo) : block_size, 16)), + page_list_(nullptr), + free_block_list_(nullptr) +{} + +void* LinkerBlockAllocator::alloc() { + if (free_block_list_ == nullptr) { + create_new_page(); + } + + FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(free_block_list_); + if (block_info->num_free_blocks > 1) { + FreeBlockInfo* next_block_info = reinterpret_cast<FreeBlockInfo*>( + reinterpret_cast<char*>(free_block_list_) + block_size_); + next_block_info->next_block = block_info->next_block; + next_block_info->num_free_blocks = block_info->num_free_blocks - 1; + free_block_list_ = next_block_info; + } else { + free_block_list_ = block_info->next_block; + } + + memset(block_info, 0, block_size_); + + return block_info; +} + +void LinkerBlockAllocator::free(void* block) { + if (block == nullptr) { + return; + } + + LinkerBlockAllocatorPage* page = find_page(block); + + if (page == nullptr) { + abort(); + } + + ssize_t offset = reinterpret_cast<uint8_t*>(block) - page->bytes; + + if (offset % block_size_ != 0) { + abort(); + } + + memset(block, 0, block_size_); + + FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(block); + + block_info->next_block = free_block_list_; + block_info->num_free_blocks = 1; + + free_block_list_ = block_info; +} + +void LinkerBlockAllocator::protect_all(int prot) { + for (LinkerBlockAllocatorPage* page = page_list_; page != nullptr; page = page->next) { + if (mprotect(page, PAGE_SIZE, prot) == -1) { + abort(); + } + } +} + +void LinkerBlockAllocator::create_new_page() { + static_assert(sizeof(LinkerBlockAllocatorPage) == PAGE_SIZE, + "Invalid sizeof(LinkerBlockAllocatorPage)"); + + LinkerBlockAllocatorPage* page = reinterpret_cast<LinkerBlockAllocatorPage*>( + mmap(nullptr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0)); + + if (page == MAP_FAILED) { + abort(); // oom + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, PAGE_SIZE, "linker_alloc"); + + memset(page, 0, PAGE_SIZE); + + FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes); + first_block->next_block = free_block_list_; + first_block->num_free_blocks = (PAGE_SIZE - sizeof(LinkerBlockAllocatorPage*))/block_size_; + + free_block_list_ = first_block; + + page->next = page_list_; + page_list_ = page; +} + +LinkerBlockAllocatorPage* LinkerBlockAllocator::find_page(void* block) { + if (block == nullptr) { + abort(); + } + + LinkerBlockAllocatorPage* page = page_list_; + while (page != nullptr) { + const uint8_t* page_ptr = reinterpret_cast<const uint8_t*>(page); + if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + PAGE_SIZE)) { + return page; + } + + page = page->next; + } + + abort(); +}
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h new file mode 100644 index 0000000..4b9b995 --- /dev/null +++ b/linker/linker_block_allocator.h
@@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKER_BLOCK_ALLOCATOR_H +#define __LINKER_BLOCK_ALLOCATOR_H + +#include <stdlib.h> +#include <limits.h> +#include "private/bionic_macros.h" + +struct LinkerBlockAllocatorPage; + +/* + * This class is a non-template version of the LinkerTypeAllocator + * It keeps code inside .cpp file by keeping the interface + * template-free. + * + * Please use LinkerTypeAllocator<type> where possible (everywhere). + */ +class LinkerBlockAllocator { + public: + explicit LinkerBlockAllocator(size_t block_size); + + void* alloc(); + void free(void* block); + void protect_all(int prot); + + private: + void create_new_page(); + LinkerBlockAllocatorPage* find_page(void* block); + + size_t block_size_; + LinkerBlockAllocatorPage* page_list_; + void* free_block_list_; + + DISALLOW_COPY_AND_ASSIGN(LinkerBlockAllocator); +}; + +/* + * A simple allocator for the dynamic linker. An allocator allocates instances + * of a single fixed-size type. Allocations are backed by page-sized private + * anonymous mmaps. + * + * The differences between this allocator and LinkerMemoryAllocator are: + * 1. This allocator manages space more efficiently. LinkerMemoryAllocator + * operates in power-of-two sized blocks up to 1k, when this implementation + * splits the page to aligned size of structure; For example for structures + * with size 513 this allocator will use 516 (520 for lp64) bytes of data + * where generalized implementation is going to use 1024 sized blocks. + * + * 2. This allocator does not munmap allocated memory, where LinkerMemoryAllocator does. + * + * 3. This allocator provides mprotect services to the user, where LinkerMemoryAllocator + * always treats it's memory as READ|WRITE. + */ +template<typename T> +class LinkerTypeAllocator { + public: + LinkerTypeAllocator() : block_allocator_(sizeof(T)) {} + T* alloc() { return reinterpret_cast<T*>(block_allocator_.alloc()); } + void free(T* t) { block_allocator_.free(t); } + void protect_all(int prot) { block_allocator_.protect_all(prot); } + private: + LinkerBlockAllocator block_allocator_; + DISALLOW_COPY_AND_ASSIGN(LinkerTypeAllocator); +}; + +#endif // __LINKER_BLOCK_ALLOCATOR_H
diff --git a/linker/linker_common_types.h b/linker/linker_common_types.h new file mode 100644 index 0000000..6afd950 --- /dev/null +++ b/linker/linker_common_types.h
@@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __LINKER_COMMON_TYPES_H +#define __LINKER_COMMON_TYPES_H + +#include <android/dlext.h> +#include "linked_list.h" + +// TODO(dimitry): move this to linker_defines.h? Unless it is removed by +// consequent refactoring steps. + +// Android uses RELA for aarch64 and x86_64. mips64 still uses REL. +#if defined(__aarch64__) || defined(__x86_64__) +#define USE_RELA 1 +#endif + + +struct soinfo; + +class SoinfoListAllocator { + public: + static LinkedListEntry<soinfo>* alloc(); + static void free(LinkedListEntry<soinfo>* entry); + + private: + // unconstructable + DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator); +}; + +class NamespaceListAllocator { + public: + static LinkedListEntry<android_namespace_t>* alloc(); + static void free(LinkedListEntry<android_namespace_t>* entry); + + private: + // unconstructable + DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceListAllocator); +}; + +typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t; +typedef LinkedList<android_namespace_t, NamespaceListAllocator> android_namespace_list_t; + +#endif /* __LINKER_COMMON_TYPES_H */
diff --git a/linker/linker_debug.h b/linker/linker_debug.h new file mode 100644 index 0000000..5af9929 --- /dev/null +++ b/linker/linker_debug.h
@@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_DEBUG_H_ +#define _LINKER_DEBUG_H_ + +// You can increase the verbosity of debug traces by defining the LD_DEBUG +// environment variable to a numeric value from 0 to 2 (corresponding to +// INFO, TRACE, and DEBUG calls in the source). This will only +// affect new processes being launched. + +// By default, traces are sent to logcat, with the "linker" tag. You can +// change this to go to stdout instead by setting the definition of +// LINKER_DEBUG_TO_LOG to 0. +#define LINKER_DEBUG_TO_LOG 1 + +#define TRACE_DEBUG 1 +#define DO_TRACE_LOOKUP 1 +#define DO_TRACE_RELO 1 +#define DO_TRACE_IFUNC 1 +#define TIMING 0 +#define STATS 0 +#define COUNT_PAGES 0 + +/********************************************************************* + * You shouldn't need to modify anything below unless you are adding + * more debugging information. + * + * To enable/disable specific debug options, change the defines above + *********************************************************************/ + +#include "private/libc_logging.h" +#include <unistd.h> + +__LIBC_HIDDEN__ extern int g_ld_debug_verbosity; + +#define CHECK(predicate) { \ + if (!(predicate)) { \ + __libc_fatal("%s:%d: %s CHECK '" #predicate "' failed", \ + __FILE__, __LINE__, __FUNCTION__); \ + } \ + } + +#if LINKER_DEBUG_TO_LOG +#define _PRINTVF(v, x...) \ + do { \ + if (g_ld_debug_verbosity > (v)) __libc_format_log(5-(v), "linker", x); \ + } while (0) +#else /* !LINKER_DEBUG_TO_LOG */ +#define _PRINTVF(v, x...) \ + do { \ + if (g_ld_debug_verbosity > (v)) { __libc_format_fd(1, x); write(1, "\n", 1); } \ + } while (0) +#endif /* !LINKER_DEBUG_TO_LOG */ + +#define PRINT(x...) _PRINTVF(-1, x) +#define INFO(x...) _PRINTVF(0, x) +#define TRACE(x...) _PRINTVF(1, x) + +#if TRACE_DEBUG +#define DEBUG(x...) _PRINTVF(2, "DEBUG: " x) +#else /* !TRACE_DEBUG */ +#define DEBUG(x...) do {} while (0) +#endif /* TRACE_DEBUG */ + +#define TRACE_TYPE(t, x...) do { if (DO_TRACE_##t) { TRACE(x); } } while (0) + +#if COUNT_PAGES +extern uint32_t bitmask[]; +#if defined(__LP64__) +#define MARK(offset) \ + do { \ + if ((((offset) >> 12) >> 5) < 4096) \ + bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \ + } while (0) +#else +#define MARK(offset) \ + do { \ + bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \ + } while (0) +#endif +#else +#define MARK(x) do {} while (0) + +#endif + +#endif /* _LINKER_DEBUG_H_ */
diff --git a/linker/linker_dlwarning.cpp b/linker/linker_dlwarning.cpp new file mode 100644 index 0000000..c53ad66 --- /dev/null +++ b/linker/linker_dlwarning.cpp
@@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_dlwarning.h" + +#include <strings.h> + +#include <string> + +static std::string current_msg; + +void add_dlwarning(const char* sopath, const char* message, const char* value) { + if (!current_msg.empty()) { + current_msg += '\n'; + } + + current_msg = current_msg + basename(sopath) + ": " + message; + + if (value != nullptr) { + current_msg = current_msg + " \"" + value + "\""; + } +} + +// Resets the current one (like dlerror but instead of +// being thread-local it is process-local). +void get_dlwarning(void* obj, void (*f)(void*, const char*)) { + if (current_msg.empty()) { + f(obj, nullptr); + } else { + std::string msg = current_msg; + current_msg.clear(); + f(obj, msg.c_str()); + } +}
diff --git a/linker/linker_dlwarning.h b/linker/linker_dlwarning.h new file mode 100644 index 0000000..0263c72 --- /dev/null +++ b/linker/linker_dlwarning.h
@@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKER_DLWARNING_H +#define __LINKER_DLWARNING_H + +void add_dlwarning(const char* sopath, const char* message, const char* value = nullptr); + +// Resets the current one (like dlerror but instead of +// being thread-local it is process-local). The user_data +// is used to avoid forcing user into saving the message +// to a global variable. +void get_dlwarning(void* user_data, void (*f)(void*, const char*)); + +#endif /* __LINKER_DLWARNING_H */
diff --git a/linker/linker_gdb_support.cpp b/linker/linker_gdb_support.cpp new file mode 100644 index 0000000..de74087 --- /dev/null +++ b/linker/linker_gdb_support.cpp
@@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_gdb_support.h" + +#include <pthread.h> + +#include "private/ScopedPthreadMutexLocker.h" + +// This function is an empty stub where GDB locates a breakpoint to get notified +// about linker activity. +extern "C" +void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(); + +r_debug _r_debug = + {1, nullptr, reinterpret_cast<uintptr_t>(&rtld_db_dlactivity), r_debug::RT_CONSISTENT, 0}; + +static pthread_mutex_t g__r_debug_mutex = PTHREAD_MUTEX_INITIALIZER; +static link_map* r_debug_tail = nullptr; + +void insert_link_map_into_debug_map(link_map* map) { + // Stick the new library at the end of the list. + // gdb tends to care more about libc than it does + // about leaf libraries, and ordering it this way + // reduces the back-and-forth over the wire. + if (r_debug_tail != nullptr) { + r_debug_tail->l_next = map; + map->l_prev = r_debug_tail; + map->l_next = nullptr; + } else { + _r_debug.r_map = map; + map->l_prev = nullptr; + map->l_next = nullptr; + } + r_debug_tail = map; +} + +void remove_link_map_from_debug_map(link_map* map) { + if (r_debug_tail == map) { + r_debug_tail = map->l_prev; + } + + if (map->l_prev) { + map->l_prev->l_next = map->l_next; + } + if (map->l_next) { + map->l_next->l_prev = map->l_prev; + } +} + +void notify_gdb_of_load(link_map* map) { + ScopedPthreadMutexLocker locker(&g__r_debug_mutex); + + _r_debug.r_state = r_debug::RT_ADD; + rtld_db_dlactivity(); + + insert_link_map_into_debug_map(map); + + _r_debug.r_state = r_debug::RT_CONSISTENT; + rtld_db_dlactivity(); +} + +void notify_gdb_of_unload(link_map* map) { + ScopedPthreadMutexLocker locker(&g__r_debug_mutex); + + _r_debug.r_state = r_debug::RT_DELETE; + rtld_db_dlactivity(); + + remove_link_map_from_debug_map(map); + + _r_debug.r_state = r_debug::RT_CONSISTENT; + rtld_db_dlactivity(); +} + +void notify_gdb_of_libraries() { + _r_debug.r_state = r_debug::RT_ADD; + rtld_db_dlactivity(); + _r_debug.r_state = r_debug::RT_CONSISTENT; + rtld_db_dlactivity(); +} +
diff --git a/linker/linker_gdb_support.h b/linker/linker_gdb_support.h new file mode 100644 index 0000000..2a590ba --- /dev/null +++ b/linker/linker_gdb_support.h
@@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __LINKER_GDB_SUPPORT_H +#define __LINKER_GDB_SUPPORT_H + +#include <link.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +void insert_link_map_into_debug_map(link_map* map); +void remove_link_map_from_debug_map(link_map* map); +void notify_gdb_of_load(link_map* map); +void notify_gdb_of_unload(link_map* map); +void notify_gdb_of_libraries(); + +extern struct r_debug _r_debug; + +__END_DECLS + +#endif
diff --git a/linker/linker_globals.cpp b/linker/linker_globals.cpp new file mode 100644 index 0000000..155ebf4 --- /dev/null +++ b/linker/linker_globals.cpp
@@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "linker_globals.h" +#include "linker_namespaces.h" + +int g_argc = 0; +char** g_argv = nullptr; +char** g_envp = nullptr; + +android_namespace_t g_default_namespace; + +std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; + +static char __linker_dl_err_buf[768]; + +char* linker_get_error_buffer() { + return &__linker_dl_err_buf[0]; +} + +size_t linker_get_error_buffer_size() { + return sizeof(__linker_dl_err_buf); +} +
diff --git a/linker/linker_globals.h b/linker/linker_globals.h new file mode 100644 index 0000000..b6f8a04 --- /dev/null +++ b/linker/linker_globals.h
@@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __LINKER_GLOBALS_H +#define __LINKER_GLOBALS_H + +#include <link.h> +#include <stddef.h> + +#include <unordered_map> + +#define DL_ERR(fmt, x...) \ + do { \ + __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \ + /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \ + LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \ + } while (false) + +#define DL_WARN(fmt, x...) \ + do { \ + __libc_format_log(ANDROID_LOG_WARN, "linker", fmt, ##x); \ + __libc_format_fd(2, "WARNING: linker: "); \ + __libc_format_fd(2, fmt, ##x); \ + __libc_format_fd(2, "\n"); \ + } while (false) + +#define DL_ERR_AND_LOG(fmt, x...) \ + do { \ + DL_ERR(fmt, x); \ + PRINT(fmt, x); \ + } while (false) + +constexpr ElfW(Versym) kVersymNotNeeded = 0; +constexpr ElfW(Versym) kVersymGlobal = 1; + +// These values are used to call constructors for .init_array && .preinit_array +extern int g_argc; +extern char** g_argv; +extern char** g_envp; + +struct soinfo; +struct android_namespace_t; + +extern android_namespace_t g_default_namespace; + +extern std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; + +// Error buffer "variable" +char* linker_get_error_buffer(); +size_t linker_get_error_buffer_size(); + +#endif /* __LINKER_GLOBALS_H */
diff --git a/linker/linker_libc_support.c b/linker/linker_libc_support.c new file mode 100644 index 0000000..77a0252 --- /dev/null +++ b/linker/linker_libc_support.c
@@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../libc/arch-common/bionic/__dso_handle.h" +#include "../libc/arch-common/bionic/pthread_atfork.h" + +int atexit(void (*function)(void) __attribute__((__unused__))) { + return -1; +} +
diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp new file mode 100644 index 0000000..a9d358a --- /dev/null +++ b/linker/linker_logger.cpp
@@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> +#include <sys/prctl.h> +#include <sys/system_properties.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "android-base/strings.h" +#include "linker_logger.h" +#include "private/libc_logging.h" + +LinkerLogger g_linker_logger; + +static const char* kSystemLdDebugProperty = "debug.ld.all"; +static const char* kLdDebugPropertyPrefix = "debug.ld.app."; + +static const char* kOptionErrors = "dlerror"; +static const char* kOptionDlopen = "dlopen"; + +static std::string property_get(const char* name) { + char value[PROP_VALUE_MAX] = {}; + __system_property_get(name, value); + return value; +} + +static uint32_t ParseProperty(const std::string& value) { + if (value.empty()) { + return 0; + } + + std::vector<std::string> options = android::base::Split(value, ","); + + uint32_t flags = 0; + + for (const auto& o : options) { + if (o == kOptionErrors) { + flags |= kLogErrors; + } else if (o == kOptionDlopen){ + flags |= kLogDlopen; + } else { + __libc_format_log(ANDROID_LOG_WARN, "linker", "Unknown debug.ld option \"%s\", will ignore.", o.c_str()); + } + } + + return flags; +} + +void LinkerLogger::ResetState() { + // the most likely scenario app is not debuggable and + // is running on user build - the logging is disabled. + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) { + return; + } + + flags_ = 0; + // check flag applied to all processes first + std::string value = property_get(kSystemLdDebugProperty); + flags_ |= ParseProperty(value); + + // get process basename + std::string process_name = basename(g_argv[0]); + + std::string property_name = std::string(kLdDebugPropertyPrefix) + process_name; + + // Property names are limited to PROP_NAME_MAX. + + if (property_name.size() >= PROP_NAME_MAX) { + size_t count = PROP_NAME_MAX - 1; + // remove trailing dots... + while (property_name[count-1] == '.') { + --count; + } + + property_name = property_name.substr(0, count); + } + value = property_get(property_name.c_str()); + flags_ |= ParseProperty(value); +} + +void LinkerLogger::Log(uint32_t type, const char* format, ...) { + if ((flags_ & type) == 0) { + return; + } + + va_list ap; + va_start(ap, format); + __libc_format_log_va_list(ANDROID_LOG_DEBUG, "linker", format, ap); + va_end(ap); +} +
diff --git a/linker/linker_logger.h b/linker/linker_logger.h new file mode 100644 index 0000000..0932471 --- /dev/null +++ b/linker/linker_logger.h
@@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_LOGGER_H_ +#define _LINKER_LOGGER_H_ + +#include <stdlib.h> +#include <limits.h> +#include "private/bionic_macros.h" + +#define LD_LOG(type, x...) \ + { \ + g_linker_logger.Log(type, x); \ + } + +constexpr const uint32_t kLogErrors = 1 << 0; +constexpr const uint32_t kLogDlopen = 1 << 1; + +class LinkerLogger { + public: + LinkerLogger() : flags_(0) { } + + void ResetState(); + void Log(uint32_t type, const char* format, ...); + private: + uint32_t flags_; + + DISALLOW_COPY_AND_ASSIGN(LinkerLogger); +}; + +extern LinkerLogger g_linker_logger; +extern char** g_argv; + +#endif /* _LINKER_LOGGER_H_ */
diff --git a/linker/linker_mapped_file_fragment.cpp b/linker/linker_mapped_file_fragment.cpp new file mode 100644 index 0000000..27c1c69 --- /dev/null +++ b/linker/linker_mapped_file_fragment.cpp
@@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_mapped_file_fragment.h" +#include "linker_debug.h" +#include "linker_utils.h" + +#include <inttypes.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> + +MappedFileFragment::MappedFileFragment() : map_start_(nullptr), map_size_(0), + data_(nullptr), size_ (0) +{ } + +MappedFileFragment::~MappedFileFragment() { + if (map_start_ != nullptr) { + munmap(map_start_, map_size_); + } +} + +bool MappedFileFragment::Map(int fd, off64_t base_offset, size_t elf_offset, size_t size) { + off64_t offset; + CHECK(safe_add(&offset, base_offset, elf_offset)); + + off64_t page_min = page_start(offset); + off64_t end_offset; + + CHECK(safe_add(&end_offset, offset, size)); + CHECK(safe_add(&end_offset, end_offset, page_offset(offset))); + + size_t map_size = static_cast<size_t>(end_offset - page_min); + CHECK(map_size >= size); + + uint8_t* map_start = static_cast<uint8_t*>( + mmap64(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min)); + + if (map_start == MAP_FAILED) { + return false; + } + + map_start_ = map_start; + map_size_ = map_size; + + data_ = map_start + page_offset(offset); + size_ = size; + + return true; +}
diff --git a/linker/linker_mapped_file_fragment.h b/linker/linker_mapped_file_fragment.h new file mode 100644 index 0000000..91bd077 --- /dev/null +++ b/linker/linker_mapped_file_fragment.h
@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LINKER_MAPPED_FILE_FRAGMENT_H +#define LINKER_MAPPED_FILE_FRAGMENT_H + +#include <unistd.h> + +#include "private/bionic_macros.h" + +class MappedFileFragment { + public: + MappedFileFragment(); + ~MappedFileFragment(); + + bool Map(int fd, off64_t base_offset, size_t elf_offset, size_t size); + + void* data() const { return data_; } + size_t size() const { return size_; } + private: + void* map_start_; + size_t map_size_; + void* data_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(MappedFileFragment); +}; + +#endif /* LINKER_MAPPED_FILE_FRAGMENT_H */
diff --git a/linker/linker_memory.cpp b/linker/linker_memory.cpp new file mode 100644 index 0000000..1892d02 --- /dev/null +++ b/linker/linker_memory.cpp
@@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_allocator.h" + +#include <stdlib.h> + +static LinkerMemoryAllocator g_linker_allocator; + +void* malloc(size_t byte_count) { + return g_linker_allocator.alloc(byte_count); +} + +void* calloc(size_t item_count, size_t item_size) { + return g_linker_allocator.alloc(item_count*item_size); +} + +void* realloc(void* p, size_t byte_count) { + return g_linker_allocator.realloc(p, byte_count); +} + +void free(void* ptr) { + g_linker_allocator.free(ptr); +} +
diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp new file mode 100644 index 0000000..cd392dc --- /dev/null +++ b/linker/linker_mips.cpp
@@ -0,0 +1,333 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(__LP64__) && __mips_isa_rev >= 5 +#include <sys/prctl.h> +#endif + +#include "linker.h" +#include "linker_debug.h" +#include "linker_globals.h" +#include "linker_phdr.h" +#include "linker_relocs.h" +#include "linker_reloc_iterators.h" +#include "linker_sleb128.h" +#include "linker_soinfo.h" + +template bool soinfo::relocate<plain_reloc_iterator>(const VersionTracker& version_tracker, + plain_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template bool soinfo::relocate<packed_reloc_iterator<sleb128_decoder>>( + const VersionTracker& version_tracker, + packed_reloc_iterator<sleb128_decoder>&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template <typename ElfRelIteratorT> +bool soinfo::relocate(const VersionTracker& version_tracker, + ElfRelIteratorT&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group) { + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { + const auto rel = rel_iterator.next(); + + if (rel == nullptr) { + return false; + } + + ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); + ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); + + ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias); + ElfW(Addr) sym_addr = 0; + const char* sym_name = nullptr; + + DEBUG("Processing \"%s\" relocation at index %zd", get_realpath(), idx); + if (type == R_GENERIC_NONE) { + continue; + } + + const ElfW(Sym)* s = nullptr; + soinfo* lsi = nullptr; + + if (sym != 0) { + sym_name = get_string(symtab_[sym].st_name); + const version_info* vi = nullptr; + + if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) { + return false; + } + + if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) { + return false; + } + + if (s == nullptr) { + // mips does not support relocation with weak-undefined symbols + DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", + sym_name, get_realpath()); + return false; + } else { + // We got a definition. + sym_addr = lsi->resolve_symbol_address(s); + } + count_relocation(kRelocSymbol); + } + + switch (type) { + case R_MIPS_REL32: +#if defined(__LP64__) + // MIPS Elf64_Rel entries contain compound relocations + // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case + if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 || + ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) { + DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)", + type, static_cast<unsigned>(ELF64_R_TYPE2(rel->r_info)), + static_cast<unsigned>(ELF64_R_TYPE3(rel->r_info)), rel, idx); + return false; + } +#endif + count_relocation(s == nullptr ? kRelocAbsolute : kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc), + static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*"); + if (s != nullptr) { + *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr; + } else { + *reinterpret_cast<ElfW(Addr)*>(reloc) += load_bias; + } + break; + default: + DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx); + return false; + } + } + return true; +} + +bool soinfo::mips_relocate_got(const VersionTracker& version_tracker, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group) { + ElfW(Addr)** got = plt_got_; + if (got == nullptr) { + return true; + } + + // got[0] is the address of the lazy resolver function. + // got[1] may be used for a GNU extension. + // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start). + // FIXME: maybe this should be in a separate routine? + if ((flags_ & FLAG_LINKER) == 0) { + size_t g = 0; + got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef); + if (reinterpret_cast<intptr_t>(got[g]) < 0) { + got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed); + } + // Relocate the local GOT entries. + for (; g < mips_local_gotno_; g++) { + got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias); + } + } + + // Now for the global GOT entries... + got = plt_got_ + mips_local_gotno_; + for (ElfW(Word) sym = mips_gotsym_; sym < mips_symtabno_; sym++, got++) { + // This is an undefined reference... try to locate it. + const ElfW(Sym)* local_sym = symtab_ + sym; + const char* sym_name = get_string(local_sym->st_name); + soinfo* lsi = nullptr; + const ElfW(Sym)* s = nullptr; + + ElfW(Word) st_visibility = (local_sym->st_other & 0x3); + + if (st_visibility == STV_DEFAULT) { + const version_info* vi = nullptr; + + if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) { + return false; + } + + if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) { + return false; + } + } else if (st_visibility == STV_PROTECTED) { + if (local_sym->st_value == 0) { + DL_ERR("%s: invalid symbol \"%s\" (PROTECTED/UNDEFINED) ", + get_realpath(), sym_name); + return false; + } + s = local_sym; + lsi = this; + } else { + DL_ERR("%s: invalid symbol \"%s\" visibility: 0x%x", + get_realpath(), sym_name, st_visibility); + return false; + } + + if (s == nullptr) { + // We only allow an undefined symbol if this is a weak reference. + if (ELF_ST_BIND(local_sym->st_info) != STB_WEAK) { + DL_ERR("%s: cannot locate \"%s\"...", get_realpath(), sym_name); + return false; + } + *got = 0; + } else { + // FIXME: is this sufficient? + // For reference see NetBSD link loader + // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup + *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s)); + } + } + return true; +} + +#if !defined(__LP64__) + +// Checks for mips32's various floating point abis. +// (Mips64 Android has a single floating point abi and doesn't need any checks) + +// Linux kernel has declarations similar to the following +// in <linux>/arch/mips/include/asm/elf.h, +// but that non-uapi internal header file will never be imported +// into bionic's kernel headers. + +#define PT_MIPS_ABIFLAGS 0x70000003 // is .MIPS.abiflags segment + +struct mips_elf_abiflags_v0 { + uint16_t version; // version of this structure + uint8_t isa_level, isa_rev, gpr_size, cpr1_size, cpr2_size; + uint8_t fp_abi; // mips32 ABI variants for floating point + uint32_t isa_ext, ases, flags1, flags2; +}; + +// Bits of flags1: +#define MIPS_AFL_FLAGS1_ODDSPREG 1 // Uses odd-numbered single-prec fp regs + +// Some values of fp_abi: via compiler flag: +#define MIPS_ABI_FP_DOUBLE 1 // -mdouble-float +#define MIPS_ABI_FP_XX 5 // -mfpxx +#define MIPS_ABI_FP_64A 7 // -mips32r* -mfp64 -mno-odd-spreg + +#if __mips_isa_rev >= 5 +static bool mips_fre_mode_on = false; // have set FRE=1 mode for process +#endif + +bool soinfo::mips_check_and_adjust_fp_modes() { + mips_elf_abiflags_v0* abiflags = nullptr; + int mips_fpabi; + + // Find soinfo's optional .MIPS.abiflags segment + for (size_t i = 0; i<phnum; ++i) { + const ElfW(Phdr)& ph = phdr[i]; + if (ph.p_type == PT_MIPS_ABIFLAGS) { + if (ph.p_filesz < sizeof (mips_elf_abiflags_v0)) { + DL_ERR("Corrupt PT_MIPS_ABIFLAGS header found \"%s\"", get_realpath()); + return false; + } + abiflags = reinterpret_cast<mips_elf_abiflags_v0*>(ph.p_vaddr + load_bias); + break; + } + } + + // FP ABI-variant compatibility checks for MIPS o32 ABI + if (abiflags == nullptr) { + // Old compilers lack the new abiflags section. + // These compilers used -mfp32 -mdouble-float -modd-spreg defaults, + // ie FP32 aka DOUBLE, using odd-numbered single-prec regs + mips_fpabi = MIPS_ABI_FP_DOUBLE; + } else { + mips_fpabi = abiflags->fp_abi; + if ( (abiflags->flags1 & MIPS_AFL_FLAGS1_ODDSPREG) + && (mips_fpabi == MIPS_ABI_FP_XX || + mips_fpabi == MIPS_ABI_FP_64A ) ) { + // Android supports fewer cases than Linux + DL_ERR("Unsupported odd-single-prec FloatPt reg uses in \"%s\"", + get_realpath()); + return false; + } + } + if (!(mips_fpabi == MIPS_ABI_FP_DOUBLE || +#if __mips_isa_rev >= 5 + mips_fpabi == MIPS_ABI_FP_64A || +#endif + mips_fpabi == MIPS_ABI_FP_XX )) { + DL_ERR("Unsupported MIPS32 FloatPt ABI %d found in \"%s\"", + mips_fpabi, get_realpath()); + return false; + } + +#if __mips_isa_rev >= 5 + // Adjust process's FR Emulation mode, if needed + // + // On Mips R5 & R6, Android runs continuously in FR=1 64bit-fpreg mode. + // NDK mips32 apps compiled with old compilers generate FP32 code + // which expects FR=0 32-bit fp registers. + // NDK mips32 apps compiled with newer compilers generate modeless + // FPXX code which runs on both FR=0 and FR=1 modes. + // Android itself is compiled in FP64A which requires FR=1 mode. + // FP32, FPXX, and FP64A all interlink okay, without dynamic FR mode + // changes during calls. For details, see + // http://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking + // Processes containing FR32 FR=0 code are run via kernel software assist, + // which maps all odd-numbered single-precision reg refs onto the + // upper half of the paired even-numbered double-precision reg. + // FRE=1 triggers traps to the kernel's emulator on every single-precision + // fp op (for both odd and even-numbered registers). + // Turning on FRE=1 traps is done at most once per process, simultanously + // for all threads of that process, when dlopen discovers FP32 code. + // The kernel repacks threads' registers when FRE mode is turn on or off. + // These asynchronous adjustments are wrong if any thread was executing + // FPXX code using odd-numbered single-precision regs. + // Current Android compilers default to the -mno-oddspreg option, + // and this requirement is checked by Android's dlopen. + // So FRE can always be safely turned on for FP32, anytime. + // Deferred enhancement: Allow loading of odd-spreg FPXX modules. + + if (mips_fpabi == MIPS_ABI_FP_DOUBLE && !mips_fre_mode_on) { + // Turn on FRE mode, which emulates mode-sensitive FR=0 code on FR=1 + // register files, by trapping to kernel on refs to single-precision regs + if (prctl(PR_SET_FP_MODE, PR_FP_MODE_FR|PR_FP_MODE_FRE)) { + DL_ERR("Kernel or cpu failed to set FRE mode required for running \"%s\"", + get_realpath()); + return false; + } + DL_WARN("Using FRE=1 mode to run \"%s\"", get_realpath()); + mips_fre_mode_on = true; // Avoid future redundant mode-switch calls + // FRE mode is never turned back off. + // Deferred enhancement: + // Reset FRE mode when dlclose() removes all FP32 modules + } +#else + // Android runs continuously in FR=0 32bit-fpreg mode. +#endif // __mips_isa_rev + return true; +} + +#endif // __LP64___
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp new file mode 100644 index 0000000..675f324 --- /dev/null +++ b/linker/linker_namespaces.cpp
@@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "linker_namespaces.h" +#include "linker_utils.h" + +#include <vector> + +bool android_namespace_t::is_accessible(const std::string& file) { + if (!is_isolated_) { + return true; + } + + for (const auto& dir : ld_library_paths_) { + if (file_is_in_dir(file, dir)) { + return true; + } + } + + for (const auto& dir : default_library_paths_) { + if (file_is_in_dir(file, dir)) { + return true; + } + } + + for (const auto& dir : permitted_paths_) { + if (file_is_under_dir(file, dir)) { + return true; + } + } + + return false; +} +
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h new file mode 100644 index 0000000..c1cee8e --- /dev/null +++ b/linker/linker_namespaces.h
@@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __LINKER_NAMESPACES_H +#define __LINKER_NAMESPACES_H + +#include "linker_common_types.h" + +#include <string> +#include <vector> + +struct android_namespace_t { + public: + android_namespace_t() : name_(nullptr), is_isolated_(false) {} + + const char* get_name() const { return name_; } + void set_name(const char* name) { name_ = name; } + + bool is_isolated() const { return is_isolated_; } + void set_isolated(bool isolated) { is_isolated_ = isolated; } + + const std::vector<std::string>& get_ld_library_paths() const { + return ld_library_paths_; + } + void set_ld_library_paths(std::vector<std::string>&& library_paths) { + ld_library_paths_ = library_paths; + } + + const std::vector<std::string>& get_default_library_paths() const { + return default_library_paths_; + } + void set_default_library_paths(std::vector<std::string>&& library_paths) { + default_library_paths_ = library_paths; + } + + const std::vector<std::string>& get_permitted_paths() const { + return permitted_paths_; + } + void set_permitted_paths(std::vector<std::string>&& permitted_paths) { + permitted_paths_ = permitted_paths; + } + + void add_soinfo(soinfo* si) { + soinfo_list_.push_back(si); + } + + void add_soinfos(const soinfo_list_t& soinfos) { + for (auto si : soinfos) { + add_soinfo(si); + } + } + + void remove_soinfo(soinfo* si) { + soinfo_list_.remove_if([&](soinfo* candidate) { + return si == candidate; + }); + } + + const soinfo_list_t& soinfo_list() const { return soinfo_list_; } + + // For isolated namespaces - checks if the file is on the search path; + // always returns true for not isolated namespace. + bool is_accessible(const std::string& path); + + private: + const char* name_; + bool is_isolated_; + std::vector<std::string> ld_library_paths_; + std::vector<std::string> default_library_paths_; + std::vector<std::string> permitted_paths_; + soinfo_list_t soinfo_list_; + + DISALLOW_COPY_AND_ASSIGN(android_namespace_t); +}; + +#endif /* __LINKER_NAMESPACES_H */
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp new file mode 100644 index 0000000..bced722 --- /dev/null +++ b/linker/linker_phdr.cpp
@@ -0,0 +1,1068 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "linker_phdr.h" + +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "linker.h" +#include "linker_globals.h" +#include "linker_debug.h" +#include "linker_utils.h" + +#include "private/bionic_prctl.h" + +static int GetTargetElfMachine() { +#if defined(__arm__) + return EM_ARM; +#elif defined(__aarch64__) + return EM_AARCH64; +#elif defined(__i386__) + return EM_386; +#elif defined(__mips__) + return EM_MIPS; +#elif defined(__x86_64__) + return EM_X86_64; +#endif +} + +/** + TECHNICAL NOTE ON ELF LOADING. + + An ELF file's program header table contains one or more PT_LOAD + segments, which corresponds to portions of the file that need to + be mapped into the process' address space. + + Each loadable segment has the following important properties: + + p_offset -> segment file offset + p_filesz -> segment file size + p_memsz -> segment memory size (always >= p_filesz) + p_vaddr -> segment's virtual address + p_flags -> segment flags (e.g. readable, writable, executable) + + We will ignore the p_paddr and p_align fields of ElfW(Phdr) for now. + + The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) + ranges of virtual addresses. A few rules apply: + + - the virtual address ranges should not overlap. + + - if a segment's p_filesz is smaller than its p_memsz, the extra bytes + between them should always be initialized to 0. + + - ranges do not necessarily start or end at page boundaries. Two distinct + segments can have their start and end on the same page. In this case, the + page inherits the mapping flags of the latter segment. + + Finally, the real load addrs of each segment is not p_vaddr. Instead the + loader decides where to load the first segment, then will load all others + relative to the first one to respect the initial range layout. + + For example, consider the following list: + + [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], + [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], + + This corresponds to two segments that cover these virtual address ranges: + + 0x30000...0x34000 + 0x40000...0x48000 + + If the loader decides to load the first segment at address 0xa0000000 + then the segments' load address ranges will be: + + 0xa0030000...0xa0034000 + 0xa0040000...0xa0048000 + + In other words, all segments must be loaded at an address that has the same + constant offset from their p_vaddr value. This offset is computed as the + difference between the first segment's load address, and its p_vaddr value. + + However, in practice, segments do _not_ start at page boundaries. Since we + can only memory-map at page boundaries, this means that the bias is + computed as: + + load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) + + (NOTE: The value must be used as a 32-bit unsigned integer, to deal with + possible wrap around UINT32_MAX for possible large p_vaddr values). + + And that the phdr0_load_address must start at a page boundary, with + the segment's real content starting at: + + phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) + + Note that ELF requires the following condition to make the mmap()-ing work: + + PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) + + The load_bias must be added to any p_vaddr value read from the ELF file to + determine the corresponding memory address. + + **/ + +#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) +#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ + MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ + MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) + +ElfReader::ElfReader() + : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0), + phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr), + strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr), + mapped_by_caller_(false) { +} + +bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) { + CHECK(!did_read_); + CHECK(!did_load_); + name_ = name; + fd_ = fd; + file_offset_ = file_offset; + file_size_ = file_size; + + if (ReadElfHeader() && + VerifyElfHeader() && + ReadProgramHeaders() && + ReadSectionHeaders() && + ReadDynamicSection()) { + did_read_ = true; + } + + return did_read_; +} + +bool ElfReader::Load(const android_dlextinfo* extinfo) { + CHECK(did_read_); + CHECK(!did_load_); + if (ReserveAddressSpace(extinfo) && + LoadSegments() && + FindPhdr()) { + did_load_ = true; + } + + return did_load_; +} + +const char* ElfReader::get_string(ElfW(Word) index) const { + CHECK(strtab_ != nullptr); + CHECK(index < strtab_size_); + + return strtab_ + index; +} + +bool ElfReader::ReadElfHeader() { + ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_)); + if (rc < 0) { + DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno)); + return false; + } + + if (rc != sizeof(header_)) { + DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_.c_str(), + static_cast<size_t>(rc)); + return false; + } + return true; +} + +bool ElfReader::VerifyElfHeader() { + if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { + DL_ERR("\"%s\" has bad ELF magic", name_.c_str()); + return false; + } + + // Try to give a clear diagnostic for ELF class mismatches, since they're + // an easy mistake to make during the 32-bit/64-bit transition period. + int elf_class = header_.e_ident[EI_CLASS]; +#if defined(__LP64__) + if (elf_class != ELFCLASS64) { + if (elf_class == ELFCLASS32) { + DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str()); + } else { + DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class); + } + return false; + } +#else + if (elf_class != ELFCLASS32) { + if (elf_class == ELFCLASS64) { + DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str()); + } else { + DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class); + } + return false; + } +#endif + + if (header_.e_ident[EI_DATA] != ELFDATA2LSB) { + DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]); + return false; + } + + if (header_.e_type != ET_DYN) { + DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type); + return false; + } + + if (header_.e_version != EV_CURRENT) { + DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version); + return false; + } + + if (header_.e_machine != GetTargetElfMachine()) { + DL_ERR("\"%s\" has unexpected e_machine: %d", name_.c_str(), header_.e_machine); + return false; + } + + if (header_.e_shentsize != sizeof(ElfW(Shdr))) { + DL_ERR("\"%s\" has unsupported e_shentsize: 0x%x (expected 0x%zx)", + name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr))); + return false; + } + + if (header_.e_shstrndx == 0) { + DL_ERR("\"%s\" has invalid e_shstrndx", name_.c_str()); + return false; + } + + return true; +} + +bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment) { + off64_t range_start; + off64_t range_end; + + // Only header can be located at the 0 offset... This function called to + // check DYNSYM and DYNAMIC sections and phdr/shdr - none of them can be + // at offset 0. + + return offset > 0 && + safe_add(&range_start, file_offset_, offset) && + safe_add(&range_end, range_start, size) && + (range_start < file_size_) && + (range_end <= file_size_) && + ((offset % alignment) == 0); +} + +// Loads the program header table from an ELF file into a read-only private +// anonymous mmap-ed block. +bool ElfReader::ReadProgramHeaders() { + phdr_num_ = header_.e_phnum; + + // Like the kernel, we only accept program header tables that + // are smaller than 64KiB. + if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) { + DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_); + return false; + } + + // Boundary checks + size_t size = phdr_num_ * sizeof(ElfW(Phdr)); + if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) { + DL_ERR_AND_LOG("\"%s\" has invalid phdr offset/size: %zu/%zu", + name_.c_str(), + static_cast<size_t>(header_.e_phoff), + size); + return false; + } + + if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) { + DL_ERR("\"%s\" phdr mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data()); + return true; +} + +bool ElfReader::ReadSectionHeaders() { + shdr_num_ = header_.e_shnum; + + if (shdr_num_ == 0) { + DL_ERR_AND_LOG("\"%s\" has no section headers", name_.c_str()); + return false; + } + + size_t size = shdr_num_ * sizeof(ElfW(Shdr)); + if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) { + DL_ERR_AND_LOG("\"%s\" has invalid shdr offset/size: %zu/%zu", + name_.c_str(), + static_cast<size_t>(header_.e_shoff), + size); + return false; + } + + if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) { + DL_ERR("\"%s\" shdr mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data()); + return true; +} + +bool ElfReader::ReadDynamicSection() { + // 1. Find .dynamic section (in section headers) + const ElfW(Shdr)* dynamic_shdr = nullptr; + for (size_t i = 0; i < shdr_num_; ++i) { + if (shdr_table_[i].sh_type == SHT_DYNAMIC) { + dynamic_shdr = &shdr_table_ [i]; + break; + } + } + + if (dynamic_shdr == nullptr) { + DL_ERR_AND_LOG("\"%s\" .dynamic section header was not found", name_.c_str()); + return false; + } + + // Make sure dynamic_shdr offset and size matches PT_DYNAMIC phdr + size_t pt_dynamic_offset = 0; + size_t pt_dynamic_filesz = 0; + for (size_t i = 0; i < phdr_num_; ++i) { + const ElfW(Phdr)* phdr = &phdr_table_[i]; + if (phdr->p_type == PT_DYNAMIC) { + pt_dynamic_offset = phdr->p_offset; + pt_dynamic_filesz = phdr->p_filesz; + } + } + + if (pt_dynamic_offset != dynamic_shdr->sh_offset) { + DL_ERR("\"%s\" .dynamic section has invalid offset: 0x%zx, " + "expected to match PT_DYNAMIC offset: 0x%zx", + name_.c_str(), + static_cast<size_t>(dynamic_shdr->sh_offset), + pt_dynamic_offset); + return false; + } + + if (pt_dynamic_filesz != dynamic_shdr->sh_size) { + DL_ERR("\"%s\" .dynamic section has invalid size: 0x%zx, " + "expected to match PT_DYNAMIC filesz: 0x%zx", + name_.c_str(), + static_cast<size_t>(dynamic_shdr->sh_size), + pt_dynamic_filesz); + return false; + } + + if (dynamic_shdr->sh_link >= shdr_num_) { + DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid sh_link: %d", + name_.c_str(), + dynamic_shdr->sh_link); + return false; + } + + const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link]; + + if (strtab_shdr->sh_type != SHT_STRTAB) { + DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)", + name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type); + return false; + } + + if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) { + DL_ERR_AND_LOG("\"%s\" has invalid offset/size of .dynamic section", name_.c_str()); + return false; + } + + if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) { + DL_ERR("\"%s\" dynamic section mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data()); + + if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) { + DL_ERR_AND_LOG("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section", + name_.c_str()); + return false; + } + + if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) { + DL_ERR("\"%s\" strtab section mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + strtab_ = static_cast<const char*>(strtab_fragment_.data()); + strtab_size_ = strtab_fragment_.size(); + return true; +} + +/* Returns the size of the extent of all the possibly non-contiguous + * loadable segments in an ELF program header table. This corresponds + * to the page-aligned size in bytes that needs to be reserved in the + * process' address space. If there are no loadable segments, 0 is + * returned. + * + * If out_min_vaddr or out_max_vaddr are not null, they will be + * set to the minimum and maximum addresses of pages to be reserved, + * or 0 if there is nothing to load. + */ +size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr)* out_min_vaddr, + ElfW(Addr)* out_max_vaddr) { + ElfW(Addr) min_vaddr = UINTPTR_MAX; + ElfW(Addr) max_vaddr = 0; + + bool found_pt_load = false; + for (size_t i = 0; i < phdr_count; ++i) { + const ElfW(Phdr)* phdr = &phdr_table[i]; + + if (phdr->p_type != PT_LOAD) { + continue; + } + found_pt_load = true; + + if (phdr->p_vaddr < min_vaddr) { + min_vaddr = phdr->p_vaddr; + } + + if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { + max_vaddr = phdr->p_vaddr + phdr->p_memsz; + } + } + if (!found_pt_load) { + min_vaddr = 0; + } + + min_vaddr = PAGE_START(min_vaddr); + max_vaddr = PAGE_END(max_vaddr); + + if (out_min_vaddr != nullptr) { + *out_min_vaddr = min_vaddr; + } + if (out_max_vaddr != nullptr) { + *out_max_vaddr = max_vaddr; + } + return max_vaddr - min_vaddr; +} + +// Reserve a virtual address range such that if it's limits were extended to the next 2**align +// boundary, it would not overlap with any existing mappings. +static void* ReserveAligned(void* hint, size_t size, size_t align) { + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + // Address hint is only used in Art for the image mapping, and it is pretty important. Don't mess + // with it. + // FIXME: try an aligned allocation and fall back to plain mmap() if the former does not provide a + // mapping at the requested address? + if (align == PAGE_SIZE || hint != nullptr) { + void* mmap_ptr = mmap(hint, size, PROT_NONE, mmap_flags, -1, 0); + if (mmap_ptr == MAP_FAILED) { + return nullptr; + } + return mmap_ptr; + } + + // Allocate enough space so that the end of the desired region aligned up is still inside the + // mapping. + size_t mmap_size = align_up(size, align) + align - PAGE_SIZE; + uint8_t* mmap_ptr = + reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0)); + if (mmap_ptr == MAP_FAILED) { + return nullptr; + } + + uint8_t* first = align_up(mmap_ptr, align); + uint8_t* last = align_down(mmap_ptr + mmap_size, align) - size; + size_t n = arc4random_uniform((last - first) / PAGE_SIZE + 1); + uint8_t* start = first + n * PAGE_SIZE; + munmap(mmap_ptr, start - mmap_ptr); + munmap(start + size, mmap_ptr + mmap_size - (start + size)); + return start; +} + +// Reserve a virtual address range big enough to hold all loadable +// segments of a program header table. This is done by creating a +// private anonymous mmap() with PROT_NONE. +bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { + ElfW(Addr) min_vaddr; + load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); + if (load_size_ == 0) { + DL_ERR("\"%s\" has no loadable segments", name_.c_str()); + return false; + } + + uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr); + void* start; + size_t reserved_size = 0; + bool reserved_hint = true; + bool strict_hint = false; + // Assume position independent executable by default. + void* mmap_hint = nullptr; + + if (extinfo != nullptr) { + if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) { + reserved_size = extinfo->reserved_size; + reserved_hint = false; + } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) { + reserved_size = extinfo->reserved_size; + } + + if (addr != nullptr && (extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) { + mmap_hint = addr; + } else if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0) { + mmap_hint = extinfo->reserved_addr; + strict_hint = true; + } + } + + if (load_size_ > reserved_size) { + if (!reserved_hint) { + DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"", + reserved_size - load_size_, load_size_, name_.c_str()); + return false; + } + start = ReserveAligned(mmap_hint, load_size_, kLibraryAlignment); + if (start == nullptr) { + DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str()); + return false; + } + if (strict_hint && (start != mmap_hint)) { + munmap(start, load_size_); + DL_ERR("couldn't reserve %zd bytes of address space at %p for \"%s\"", + load_size_, mmap_hint, name_.c_str()); + return false; + } + } else { + start = extinfo->reserved_addr; + mapped_by_caller_ = true; + } + + load_start_ = start; + load_bias_ = reinterpret_cast<uint8_t*>(start) - addr; + return true; +} + +bool ElfReader::LoadSegments() { + for (size_t i = 0; i < phdr_num_; ++i) { + const ElfW(Phdr)* phdr = &phdr_table_[i]; + + if (phdr->p_type != PT_LOAD) { + continue; + } + + // Segment addresses in memory. + ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; + ElfW(Addr) seg_end = seg_start + phdr->p_memsz; + + ElfW(Addr) seg_page_start = PAGE_START(seg_start); + ElfW(Addr) seg_page_end = PAGE_END(seg_end); + + ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz; + + // File offsets. + ElfW(Addr) file_start = phdr->p_offset; + ElfW(Addr) file_end = file_start + phdr->p_filesz; + + ElfW(Addr) file_page_start = PAGE_START(file_start); + ElfW(Addr) file_length = file_end - file_page_start; + + if (file_size_ <= 0) { + DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_); + return false; + } + + if (file_end > static_cast<size_t>(file_size_)) { + DL_ERR("invalid ELF file \"%s\" load segment[%zd]:" + " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")", + name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset), + reinterpret_cast<void*>(phdr->p_filesz), + reinterpret_cast<void*>(file_end), file_size_); + return false; + } + + if (file_length != 0) { + int prot = PFLAGS_TO_PROT(phdr->p_flags); + // W + E PT_LOAD segments are not allowed. + if ((prot & (PROT_EXEC | PROT_WRITE)) == (PROT_EXEC | PROT_WRITE)) { + DL_ERR_AND_LOG("\"%s\": W + E load segments are not allowed", name_.c_str()); + return false; + } + + void* seg_addr = mmap64(reinterpret_cast<void*>(seg_page_start), + file_length, + prot, + MAP_FIXED|MAP_PRIVATE, + fd_, + file_offset_ + file_page_start); + if (seg_addr == MAP_FAILED) { + DL_ERR("couldn't map \"%s\" segment %zd: %s", name_.c_str(), i, strerror(errno)); + return false; + } + } + + // if the segment is writable, and does not end on a page boundary, + // zero-fill it until the page limit. + if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { + memset(reinterpret_cast<void*>(seg_file_end), 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); + } + + seg_file_end = PAGE_END(seg_file_end); + + // seg_file_end is now the first page address after the file + // content. If seg_end is larger, we need to zero anything + // between them. This is done by using a private anonymous + // map for all extra pages. + if (seg_page_end > seg_file_end) { + size_t zeromap_size = seg_page_end - seg_file_end; + void* zeromap = mmap(reinterpret_cast<void*>(seg_file_end), + zeromap_size, + PFLAGS_TO_PROT(phdr->p_flags), + MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, + -1, + 0); + if (zeromap == MAP_FAILED) { + DL_ERR("couldn't zero fill \"%s\" gap: %s", name_.c_str(), strerror(errno)); + return false; + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size, ".bss"); + } + } + return true; +} + +/* Used internally. Used to set the protection bits of all loaded segments + * with optional extra flags (i.e. really PROT_WRITE). Used by + * phdr_table_protect_segments and phdr_table_unprotect_segments. + */ +static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int extra_prot_flags) { + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + + for (; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) { + continue; + } + + ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + int prot = PFLAGS_TO_PROT(phdr->p_flags); + if ((extra_prot_flags & PROT_WRITE) != 0) { + // make sure we're never simultaneously writable / executable + prot &= ~PROT_EXEC; + } + + int ret = mprotect(reinterpret_cast<void*>(seg_page_start), + seg_page_end - seg_page_start, + prot | extra_prot_flags); + if (ret < 0) { + return -1; + } + } + return 0; +} + +/* Restore the original protection modes for all loadable segments. + * You should only call this after phdr_table_unprotect_segments and + * applying all relocations. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { + return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); +} + +/* Change the protection of all loaded segments in memory to writable. + * This is useful before performing relocations. Once completed, you + * will have to call phdr_table_protect_segments to restore the original + * protection flags on all segments. + * + * Note that some writable segments can also have their content turned + * to read-only by calling phdr_table_protect_gnu_relro. This is no + * performed here. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { + return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); +} + +/* Used internally by phdr_table_protect_gnu_relro and + * phdr_table_unprotect_gnu_relro. + */ +static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int prot_flags) { + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_GNU_RELRO) { + continue; + } + + // Tricky: what happens when the relro segment does not start + // or end at page boundaries? We're going to be over-protective + // here and put every page touched by the segment as read-only. + + // This seems to match Ian Lance Taylor's description of the + // feature at http://www.airs.com/blog/archives/189. + + // Extract: + // Note that the current dynamic linker code will only work + // correctly if the PT_GNU_RELRO segment starts on a page + // boundary. This is because the dynamic linker rounds the + // p_vaddr field down to the previous page boundary. If + // there is anything on the page which should not be read-only, + // the program is likely to fail at runtime. So in effect the + // linker must only emit a PT_GNU_RELRO segment if it ensures + // that it starts on a page boundary. + ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + int ret = mprotect(reinterpret_cast<void*>(seg_page_start), + seg_page_end - seg_page_start, + prot_flags); + if (ret < 0) { + return -1; + } + } + return 0; +} + +/* Apply GNU relro protection if specified by the program header. This will + * turn some of the pages of a writable PT_LOAD segment to read-only, as + * specified by one or more PT_GNU_RELRO segments. This must be always + * performed after relocations. + * + * The areas typically covered are .got and .data.rel.ro, these are + * read-only from the program's POV, but contain absolute addresses + * that need to be relocated before use. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { + return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ); +} + +/* Serialize the GNU relro segments to the given file descriptor. This can be + * performed after relocations to allow another process to later share the + * relocated segment, if it was loaded at the same address. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * fd -> writable file descriptor to use + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr) load_bias, + int fd) { + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + ssize_t file_offset = 0; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_GNU_RELRO) { + continue; + } + + ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + ssize_t size = seg_page_end - seg_page_start; + + ssize_t written = TEMP_FAILURE_RETRY(write(fd, reinterpret_cast<void*>(seg_page_start), size)); + if (written != size) { + return -1; + } + void* map = mmap(reinterpret_cast<void*>(seg_page_start), size, PROT_READ, + MAP_PRIVATE|MAP_FIXED, fd, file_offset); + if (map == MAP_FAILED) { + return -1; + } + file_offset += size; + } + return 0; +} + +/* Where possible, replace the GNU relro segments with mappings of the given + * file descriptor. This can be performed after relocations to allow a file + * previously created by phdr_table_serialize_gnu_relro in another process to + * replace the dirty relocated pages, saving memory, if it was loaded at the + * same address. We have to compare the data before we map over it, since some + * parts of the relro segment may not be identical due to other libraries in + * the process being loaded at different addresses. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * fd -> readable file descriptor to use + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr) load_bias, + int fd) { + // Map the file at a temporary location so we can compare its contents. + struct stat file_stat; + if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) { + return -1; + } + off_t file_size = file_stat.st_size; + void* temp_mapping = nullptr; + if (file_size > 0) { + temp_mapping = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (temp_mapping == MAP_FAILED) { + return -1; + } + } + size_t file_offset = 0; + + // Iterate over the relro segments and compare/remap the pages. + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_GNU_RELRO) { + continue; + } + + ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + char* file_base = static_cast<char*>(temp_mapping) + file_offset; + char* mem_base = reinterpret_cast<char*>(seg_page_start); + size_t match_offset = 0; + size_t size = seg_page_end - seg_page_start; + + if (file_size - file_offset < size) { + // File is too short to compare to this segment. The contents are likely + // different as well (it's probably for a different library version) so + // just don't bother checking. + break; + } + + while (match_offset < size) { + // Skip over dissimilar pages. + while (match_offset < size && + memcmp(mem_base + match_offset, file_base + match_offset, PAGE_SIZE) != 0) { + match_offset += PAGE_SIZE; + } + + // Count similar pages. + size_t mismatch_offset = match_offset; + while (mismatch_offset < size && + memcmp(mem_base + mismatch_offset, file_base + mismatch_offset, PAGE_SIZE) == 0) { + mismatch_offset += PAGE_SIZE; + } + + // Map over similar pages. + if (mismatch_offset > match_offset) { + void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset, + PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, match_offset); + if (map == MAP_FAILED) { + munmap(temp_mapping, file_size); + return -1; + } + } + + match_offset = mismatch_offset; + } + + // Add to the base file offset in case there are multiple relro segments. + file_offset += size; + } + munmap(temp_mapping, file_size); + return 0; +} + + +#if defined(__arm__) + +# ifndef PT_ARM_EXIDX +# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ +# endif + +/* Return the address and size of the .ARM.exidx section in memory, + * if present. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Output: + * arm_exidx -> address of table in memory (null on failure). + * arm_exidx_count -> number of items in table (0 on failure). + * Return: + * 0 on error, -1 on failure (_no_ error code in errno) + */ +int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, + ElfW(Addr)** arm_exidx, size_t* arm_exidx_count) { + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_ARM_EXIDX) { + continue; + } + + *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr); + *arm_exidx_count = phdr->p_memsz / 8; + return 0; + } + *arm_exidx = nullptr; + *arm_exidx_count = 0; + return -1; +} +#endif + +/* Return the address and size of the ELF file's .dynamic section in memory, + * or null if missing. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Output: + * dynamic -> address of table in memory (null on failure). + * dynamic_flags -> protection flags for section (unset on failure) + * Return: + * void + */ +void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, + ElfW(Word)* dynamic_flags) { + *dynamic = nullptr; + for (size_t i = 0; i<phdr_count; ++i) { + const ElfW(Phdr)& phdr = phdr_table[i]; + if (phdr.p_type == PT_DYNAMIC) { + *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr.p_vaddr); + if (dynamic_flags) { + *dynamic_flags = phdr.p_flags; + } + return; + } + } +} + +/* Return the program interpreter string, or nullptr if missing. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * pointer to the program interpreter string. + */ +const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count, + ElfW(Addr) load_bias) { + for (size_t i = 0; i<phdr_count; ++i) { + const ElfW(Phdr)& phdr = phdr_table[i]; + if (phdr.p_type == PT_INTERP) { + return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr); + } + } + return nullptr; +} + +// Sets loaded_phdr_ to the address of the program header table as it appears +// in the loaded segments in memory. This is in contrast with phdr_table_, +// which is temporary and will be released before the library is relocated. +bool ElfReader::FindPhdr() { + const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; + + // If there is a PT_PHDR, use it directly. + for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { + if (phdr->p_type == PT_PHDR) { + return CheckPhdr(load_bias_ + phdr->p_vaddr); + } + } + + // Otherwise, check the first loadable segment. If its file offset + // is 0, it starts with the ELF header, and we can trivially find the + // loaded program header from it. + for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { + if (phdr->p_type == PT_LOAD) { + if (phdr->p_offset == 0) { + ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr; + const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr); + ElfW(Addr) offset = ehdr->e_phoff; + return CheckPhdr(reinterpret_cast<ElfW(Addr)>(ehdr) + offset); + } + break; + } + } + + DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str()); + return false; +} + +// Ensures that our program header is actually within a loadable +// segment. This should help catch badly-formed ELF files that +// would cause the linker to crash later when trying to access it. +bool ElfReader::CheckPhdr(ElfW(Addr) loaded) { + const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; + ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr))); + for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { + if (phdr->p_type != PT_LOAD) { + continue; + } + ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; + ElfW(Addr) seg_end = phdr->p_filesz + seg_start; + if (seg_start <= loaded && loaded_end <= seg_end) { + loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded); + return true; + } + } + DL_ERR("\"%s\" loaded phdr %p not in loadable segment", + name_.c_str(), reinterpret_cast<void*>(loaded)); + return false; +}
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h new file mode 100644 index 0000000..d6276ed --- /dev/null +++ b/linker/linker_phdr.h
@@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef LINKER_PHDR_H +#define LINKER_PHDR_H + +/* Declarations related to the ELF program header table and segments. + * + * The design goal is to provide an API that is as close as possible + * to the ELF spec, and does not depend on linker-specific data + * structures (e.g. the exact layout of struct soinfo). + */ + +#include "linker.h" +#include "linker_mapped_file_fragment.h" + +class ElfReader { + public: + ElfReader(); + + bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size); + bool Load(const android_dlextinfo* extinfo); + + const char* name() const { return name_.c_str(); } + size_t phdr_count() const { return phdr_num_; } + ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); } + size_t load_size() const { return load_size_; } + ElfW(Addr) load_bias() const { return load_bias_; } + const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; } + const ElfW(Dyn)* dynamic() const { return dynamic_; } + const char* get_string(ElfW(Word) index) const; + bool is_mapped_by_caller() const { return mapped_by_caller_; } + + private: + bool ReadElfHeader(); + bool VerifyElfHeader(); + bool ReadProgramHeaders(); + bool ReadSectionHeaders(); + bool ReadDynamicSection(); + bool ReserveAddressSpace(const android_dlextinfo* extinfo); + bool LoadSegments(); + bool FindPhdr(); + bool CheckPhdr(ElfW(Addr)); + bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment); + + bool did_read_; + bool did_load_; + std::string name_; + int fd_; + off64_t file_offset_; + off64_t file_size_; + + ElfW(Ehdr) header_; + size_t phdr_num_; + + MappedFileFragment phdr_fragment_; + const ElfW(Phdr)* phdr_table_; + + MappedFileFragment shdr_fragment_; + const ElfW(Shdr)* shdr_table_; + size_t shdr_num_; + + MappedFileFragment dynamic_fragment_; + const ElfW(Dyn)* dynamic_; + + MappedFileFragment strtab_fragment_; + const char* strtab_; + size_t strtab_size_; + + // First page of reserved address space. + void* load_start_; + // Size in bytes of reserved address space. + size_t load_size_; + // Load bias. + ElfW(Addr) load_bias_; + + // Loaded phdr. + const ElfW(Phdr)* loaded_phdr_; + + // Is map owned by the caller + bool mapped_by_caller_; +}; + +size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); + +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias); + +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); + +int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); + +int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int fd); + +int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int fd); + +#if defined(__arm__) +int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, + ElfW(Addr)** arm_exidx, size_t* arm_exidix_count); +#endif + +void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, + ElfW(Word)* dynamic_flags); + +const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); + +#endif /* LINKER_PHDR_H */
diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h new file mode 100644 index 0000000..f28c0e0 --- /dev/null +++ b/linker/linker_reloc_iterators.h
@@ -0,0 +1,165 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKER_RELOC_ITERATORS_H +#define __LINKER_RELOC_ITERATORS_H + +#include "linker.h" + +#include <string.h> + +const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; +const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; +const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; +const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; + +class plain_reloc_iterator { +#if defined(USE_RELA) + typedef ElfW(Rela) rel_t; +#else + typedef ElfW(Rel) rel_t; +#endif + public: + plain_reloc_iterator(rel_t* rel_array, size_t count) + : begin_(rel_array), end_(begin_ + count), current_(begin_) {} + + bool has_next() { + return current_ < end_; + } + + rel_t* next() { + return current_++; + } + private: + rel_t* const begin_; + rel_t* const end_; + rel_t* current_; + + DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator); +}; + +template <typename decoder_t> +class packed_reloc_iterator { +#if defined(USE_RELA) + typedef ElfW(Rela) rel_t; +#else + typedef ElfW(Rel) rel_t; +#endif + public: + explicit packed_reloc_iterator(decoder_t&& decoder) + : decoder_(decoder) { + // initialize fields + memset(&reloc_, 0, sizeof(reloc_)); + relocation_count_ = decoder_.pop_front(); + reloc_.r_offset = decoder_.pop_front(); + relocation_index_ = 0; + relocation_group_index_ = 0; + group_size_ = 0; + } + + bool has_next() const { + return relocation_index_ < relocation_count_; + } + + rel_t* next() { + if (relocation_group_index_ == group_size_) { + if (!read_group_fields()) { + // Iterator is inconsistent state; it should not be called again + // but in case it is let's make sure has_next() returns false. + relocation_index_ = relocation_count_ = 0; + return nullptr; + } + } + + if (is_relocation_grouped_by_offset_delta()) { + reloc_.r_offset += group_r_offset_delta_; + } else { + reloc_.r_offset += decoder_.pop_front(); + } + + if (!is_relocation_grouped_by_info()) { + reloc_.r_info = decoder_.pop_front(); + } + +#if defined(USE_RELA) + if (is_relocation_group_has_addend() && + !is_relocation_grouped_by_addend()) { + reloc_.r_addend += decoder_.pop_front(); + } +#endif + + relocation_index_++; + relocation_group_index_++; + + return &reloc_; + } + private: + bool read_group_fields() { + group_size_ = decoder_.pop_front(); + group_flags_ = decoder_.pop_front(); + + if (is_relocation_grouped_by_offset_delta()) { + group_r_offset_delta_ = decoder_.pop_front(); + } + + if (is_relocation_grouped_by_info()) { + reloc_.r_info = decoder_.pop_front(); + } + + if (is_relocation_group_has_addend() && + is_relocation_grouped_by_addend()) { +#if !defined(USE_RELA) + // This platform does not support rela, and yet we have it encoded in android_rel section. + DL_ERR("unexpected r_addend in android.rel section"); + return false; +#else + reloc_.r_addend += decoder_.pop_front(); + } else if (!is_relocation_group_has_addend()) { + reloc_.r_addend = 0; +#endif + } + + relocation_group_index_ = 0; + return true; + } + + bool is_relocation_grouped_by_info() { + return (group_flags_ & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; + } + + bool is_relocation_grouped_by_offset_delta() { + return (group_flags_ & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; + } + + bool is_relocation_grouped_by_addend() { + return (group_flags_ & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; + } + + bool is_relocation_group_has_addend() { + return (group_flags_ & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; + } + + decoder_t decoder_; + size_t relocation_count_; + size_t group_size_; + size_t group_flags_; + size_t group_r_offset_delta_; + size_t relocation_index_; + size_t relocation_group_index_; + rel_t reloc_; +}; + +#endif // __LINKER_RELOC_ITERATORS_H
diff --git a/linker/linker_relocs.h b/linker/linker_relocs.h new file mode 100644 index 0000000..12c1497 --- /dev/null +++ b/linker/linker_relocs.h
@@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LINKER_RELOCS_H +#define __LINKER_RELOCS_H + +#include <elf.h> + +#define R_GENERIC_NONE 0 // R_*_NONE is always 0 + +#if defined (__aarch64__) + +#define R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT +#define R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT +#define R_GENERIC_RELATIVE R_AARCH64_RELATIVE +#define R_GENERIC_IRELATIVE R_AARCH64_IRELATIVE + +#elif defined (__arm__) + +#define R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT +#define R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT +#define R_GENERIC_RELATIVE R_ARM_RELATIVE +#define R_GENERIC_IRELATIVE R_ARM_IRELATIVE + +#elif defined (__i386__) + +#define R_GENERIC_JUMP_SLOT R_386_JMP_SLOT +#define R_GENERIC_GLOB_DAT R_386_GLOB_DAT +#define R_GENERIC_RELATIVE R_386_RELATIVE +#define R_GENERIC_IRELATIVE R_386_IRELATIVE + +#elif defined (__x86_64__) + +#define R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT +#define R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT +#define R_GENERIC_RELATIVE R_X86_64_RELATIVE +#define R_GENERIC_IRELATIVE R_X86_64_IRELATIVE + +#endif + +#endif // __LINKER_RELOCS_H
diff --git a/linker/linker_sdk_versions.cpp b/linker/linker_sdk_versions.cpp new file mode 100644 index 0000000..9aebb06 --- /dev/null +++ b/linker/linker_sdk_versions.cpp
@@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker.h" +#include <android/api-level.h> +#include <atomic> + +static std::atomic<uint32_t> g_target_sdk_version(__ANDROID_API__); + +void set_application_target_sdk_version(uint32_t target) { + // translate current sdk_version to platform sdk_version + if (target == 0) { + target = __ANDROID_API__; + } + g_target_sdk_version = target; +} + +uint32_t get_application_target_sdk_version() { + return g_target_sdk_version; +} +
diff --git a/linker/linker_sleb128.h b/linker/linker_sleb128.h new file mode 100644 index 0000000..c4df259 --- /dev/null +++ b/linker/linker_sleb128.h
@@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LINKER_SLEB128_H +#define _LINKER_SLEB128_H + +#include <stdint.h> + +#include "linker_debug.h" + +// Helper classes for decoding LEB128, used in packed relocation data. +// http://en.wikipedia.org/wiki/LEB128 + +class sleb128_decoder { + public: + sleb128_decoder(const uint8_t* buffer, size_t count) + : current_(buffer), end_(buffer+count) { } + + size_t pop_front() { + size_t value = 0; + static const size_t size = CHAR_BIT * sizeof(value); + + size_t shift = 0; + uint8_t byte; + + do { + if (current_ >= end_) { + __libc_fatal("sleb128_decoder ran out of bounds"); + } + byte = *current_++; + value |= (static_cast<size_t>(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + if (shift < size && (byte & 64)) { + value |= -(static_cast<size_t>(1) << shift); + } + + return value; + } + + private: + const uint8_t* current_; + const uint8_t* const end_; +}; + +#endif // __LINKER_SLEB128_H
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp new file mode 100644 index 0000000..59bdc4d --- /dev/null +++ b/linker/linker_soinfo.cpp
@@ -0,0 +1,795 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "linker_soinfo.h" + +#include <dlfcn.h> +#include <elf.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "linker_debug.h" +#include "linker_globals.h" +#include "linker_logger.h" +#include "linker_utils.h" + +// TODO(dimitry): These functions are currently located in linker.cpp - find a better place for it +bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym); +ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr); +uint32_t get_application_target_sdk_version(); + +soinfo::soinfo(android_namespace_t* ns, const char* realpath, + const struct stat* file_stat, off64_t file_offset, + int rtld_flags) { + memset(this, 0, sizeof(*this)); + + if (realpath != nullptr) { + realpath_ = realpath; + } + + flags_ = FLAG_NEW_SOINFO; + version_ = SOINFO_VERSION; + + if (file_stat != nullptr) { + this->st_dev_ = file_stat->st_dev; + this->st_ino_ = file_stat->st_ino; + this->file_offset_ = file_offset; + } + + this->rtld_flags_ = rtld_flags; + this->primary_namespace_ = ns; +} + +soinfo::~soinfo() { + g_soinfo_handles_map.erase(handle_); +} + +void soinfo::set_dt_runpath(const char* path) { + if (!has_min_version(3)) { + return; + } + + std::vector<std::string> runpaths; + + split_path(path, ":", &runpaths); + + std::string origin = dirname(get_realpath()); + // FIXME: add $LIB and $PLATFORM. + std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}}; + for (auto&& s : runpaths) { + size_t pos = 0; + while (pos < s.size()) { + pos = s.find("$", pos); + if (pos == std::string::npos) break; + for (const auto& subst : substs) { + const std::string& token = subst.first; + const std::string& replacement = subst.second; + if (s.substr(pos + 1, token.size()) == token) { + s.replace(pos, token.size() + 1, replacement); + // -1 to compensate for the ++pos below. + pos += replacement.size() - 1; + break; + } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") { + s.replace(pos, token.size() + 3, replacement); + pos += replacement.size() - 1; + break; + } + } + // Skip $ in case it did not match any of the known substitutions. + ++pos; + } + } + + resolve_paths(runpaths, &dt_runpath_); +} + +const ElfW(Versym)* soinfo::get_versym(size_t n) const { + if (has_min_version(2) && versym_ != nullptr) { + return versym_ + n; + } + + return nullptr; +} + +ElfW(Addr) soinfo::get_verneed_ptr() const { + if (has_min_version(2)) { + return verneed_ptr_; + } + + return 0; +} + +size_t soinfo::get_verneed_cnt() const { + if (has_min_version(2)) { + return verneed_cnt_; + } + + return 0; +} + +ElfW(Addr) soinfo::get_verdef_ptr() const { + if (has_min_version(2)) { + return verdef_ptr_; + } + + return 0; +} + +size_t soinfo::get_verdef_cnt() const { + if (has_min_version(2)) { + return verdef_cnt_; + } + + return 0; +} + +bool soinfo::find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi, + const ElfW(Sym)** symbol) const { + uint32_t symbol_index; + bool success = + is_gnu_hash() ? + gnu_lookup(symbol_name, vi, &symbol_index) : + elf_lookup(symbol_name, vi, &symbol_index); + + if (success) { + *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index; + } + + return success; +} + +static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { + if (ELF_ST_BIND(s->st_info) == STB_GLOBAL || + ELF_ST_BIND(s->st_info) == STB_WEAK) { + return s->st_shndx != SHN_UNDEF; + } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) { + DL_WARN("unexpected ST_BIND value: %d for \"%s\" in \"%s\"", + ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath()); + } + + return false; +} + +static const ElfW(Versym) kVersymHiddenBit = 0x8000; + +static inline bool is_versym_hidden(const ElfW(Versym)* versym) { + // the symbol is hidden if bit 15 of versym is set. + return versym != nullptr && (*versym & kVersymHiddenBit) != 0; +} + +static inline bool check_symbol_version(const ElfW(Versym) verneed, + const ElfW(Versym)* verdef) { + return verneed == kVersymNotNeeded || + verdef == nullptr || + verneed == (*verdef & ~kVersymHiddenBit); +} + +bool soinfo::gnu_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) const { + uint32_t hash = symbol_name.gnu_hash(); + uint32_t h2 = hash >> gnu_shift2_; + + uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8; + uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_; + ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num]; + + *symbol_index = 0; + + TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + // test against bloom filter + if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + return true; + } + + // bloom test says "probably yes"... + uint32_t n = gnu_bucket_[hash % gnu_nbucket_]; + + if (n == 0) { + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + return true; + } + + // lookup versym for the version definition in this library + // note the difference between "version is not requested" (vi == nullptr) + // and "version not found". In the first case verneed is kVersymNotNeeded + // which implies that the default version can be accepted; the second case results in + // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols + // for this library and consider only *global* ones. + ElfW(Versym) verneed = 0; + if (!find_verdef_version_index(this, vi, &verneed)) { + return false; + } + + do { + ElfW(Sym)* s = symtab_ + n; + const ElfW(Versym)* verdef = get_versym(n); + // skip hidden versions when verneed == kVersymNotNeeded (0) + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } + if (((gnu_chain_[n] ^ hash) >> 1) == 0 && + check_symbol_version(verneed, verdef) && + strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + is_symbol_global_and_defined(this, s)) { + TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value), + static_cast<size_t>(s->st_size)); + *symbol_index = n; + return true; + } + } while ((gnu_chain_[n++] & 1) == 0); + + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + return true; +} + +bool soinfo::elf_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) const { + uint32_t hash = symbol_name.elf_hash(); + + TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd", + symbol_name.get_name(), get_realpath(), + reinterpret_cast<void*>(base), hash, hash % nbucket_); + + ElfW(Versym) verneed = 0; + if (!find_verdef_version_index(this, vi, &verneed)) { + return false; + } + + for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { + ElfW(Sym)* s = symtab_ + n; + const ElfW(Versym)* verdef = get_versym(n); + + // skip hidden versions when verneed == 0 + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } + + if (check_symbol_version(verneed, verdef) && + strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + is_symbol_global_and_defined(this, s)) { + TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", + symbol_name.get_name(), get_realpath(), + reinterpret_cast<void*>(s->st_value), + static_cast<size_t>(s->st_size)); + *symbol_index = n; + return true; + } + } + + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", + symbol_name.get_name(), get_realpath(), + reinterpret_cast<void*>(base), hash, hash % nbucket_); + + *symbol_index = 0; + return true; +} + +ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) { + return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr); +} + +static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) { + return sym->st_shndx != SHN_UNDEF && + soaddr >= sym->st_value && + soaddr < sym->st_value + sym->st_size; +} + +ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { + ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; + + for (size_t i = 0; i < gnu_nbucket_; ++i) { + uint32_t n = gnu_bucket_[i]; + + if (n == 0) { + continue; + } + + do { + ElfW(Sym)* sym = symtab_ + n; + if (symbol_matches_soaddr(sym, soaddr)) { + return sym; + } + } while ((gnu_chain_[n++] & 1) == 0); + } + + return nullptr; +} + +ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) { + ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; + + // Search the library's symbol table for any defined symbol which + // contains this address. + for (size_t i = 0; i < nchain_; ++i) { + ElfW(Sym)* sym = symtab_ + i; + if (symbol_matches_soaddr(sym, soaddr)) { + return sym; + } + } + + return nullptr; +} + +static void call_function(const char* function_name __unused, + linker_ctor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { + return; + } + + TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(g_argc, g_argv, g_envp); + TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +static void call_function(const char* function_name __unused, + linker_dtor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { + return; + } + + TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(); + TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +template <typename F> +static void call_array(const char* array_name __unused, + F* functions, + size_t count, + bool reverse, + const char* realpath) { + if (functions == nullptr) { + return; + } + + TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath); + + int begin = reverse ? (count - 1) : 0; + int end = reverse ? -1 : count; + int step = reverse ? -1 : 1; + + for (int i = begin; i != end; i += step) { + TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]); + call_function("function", functions[i], realpath); + } + + TRACE("[ Done calling %s for '%s' ]", array_name, realpath); +} + +void soinfo::call_pre_init_constructors() { + // DT_PREINIT_ARRAY functions are called before any other constructors for executables, + // but ignored in a shared library. + call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath()); +} + +void soinfo::call_constructors() { + if (constructors_called) { + return; + } + + // We set constructors_called before actually calling the constructors, otherwise it doesn't + // protect against recursive constructor calls. One simple example of constructor recursion + // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so: + // 1. The program depends on libc, so libc's constructor is called here. + // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. + // 3. dlopen() calls the constructors on the newly created + // soinfo for libc_malloc_debug_leak.so. + // 4. The debug .so depends on libc, so CallConstructors is + // called again with the libc soinfo. If it doesn't trigger the early- + // out above, the libc constructor will be called again (recursively!). + constructors_called = true; + + if (!is_main_executable() && preinit_array_ != nullptr) { + // The GNU dynamic linker silently ignores these, but we warn the developer. + PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath()); + } + + get_children().for_each([] (soinfo* si) { + si->call_constructors(); + }); + + TRACE("\"%s\": calling constructors", get_realpath()); + + // DT_INIT should be called before DT_INIT_ARRAY if both are present. + call_function("DT_INIT", init_func_, get_realpath()); + call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath()); +} + +void soinfo::call_destructors() { + if (!constructors_called) { + return; + } + TRACE("\"%s\": calling destructors", get_realpath()); + + // DT_FINI_ARRAY must be parsed in reverse order. + call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath()); + + // DT_FINI should be called after DT_FINI_ARRAY if both are present. + call_function("DT_FINI", fini_func_, get_realpath()); +} + +void soinfo::add_child(soinfo* child) { + if (has_min_version(0)) { + child->parents_.push_back(this); + this->children_.push_back(child); + } +} + +void soinfo::remove_all_links() { + if (!has_min_version(0)) { + return; + } + + // 1. Untie connected soinfos from 'this'. + children_.for_each([&] (soinfo* child) { + child->parents_.remove_if([&] (const soinfo* parent) { + return parent == this; + }); + }); + + parents_.for_each([&] (soinfo* parent) { + parent->children_.remove_if([&] (const soinfo* child) { + return child == this; + }); + }); + + // 2. Remove from the primary namespace + primary_namespace_->remove_soinfo(this); + primary_namespace_ = nullptr; + + // 3. Remove from secondary namespaces + secondary_namespaces_.for_each([&](android_namespace_t* ns) { + ns->remove_soinfo(this); + }); + + + // 4. Once everything untied - clear local lists. + parents_.clear(); + children_.clear(); + secondary_namespaces_.clear(); +} + +dev_t soinfo::get_st_dev() const { + if (has_min_version(0)) { + return st_dev_; + } + + return 0; +}; + +ino_t soinfo::get_st_ino() const { + if (has_min_version(0)) { + return st_ino_; + } + + return 0; +} + +off64_t soinfo::get_file_offset() const { + if (has_min_version(1)) { + return file_offset_; + } + + return 0; +} + +uint32_t soinfo::get_rtld_flags() const { + if (has_min_version(1)) { + return rtld_flags_; + } + + return 0; +} + +uint32_t soinfo::get_dt_flags_1() const { + if (has_min_version(1)) { + return dt_flags_1_; + } + + return 0; +} + +void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { + if (has_min_version(1)) { + if ((dt_flags_1 & DF_1_GLOBAL) != 0) { + rtld_flags_ |= RTLD_GLOBAL; + } + + if ((dt_flags_1 & DF_1_NODELETE) != 0) { + rtld_flags_ |= RTLD_NODELETE; + } + + dt_flags_1_ = dt_flags_1; + } +} + +void soinfo::set_nodelete() { + rtld_flags_ |= RTLD_NODELETE; +} + +const char* soinfo::get_realpath() const { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + return realpath_.c_str(); + } else { + return old_name_; + } +#else + return realpath_.c_str(); +#endif +} + +void soinfo::set_soname(const char* soname) { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + soname_ = soname; + } + strlcpy(old_name_, soname_, sizeof(old_name_)); +#else + soname_ = soname; +#endif +} + +const char* soinfo::get_soname() const { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + return soname_; + } else { + return old_name_; + } +#else + return soname_; +#endif +} + +// This is a return on get_children()/get_parents() if +// 'this->flags' does not have FLAG_NEW_SOINFO set. +static soinfo_list_t g_empty_list; + +soinfo_list_t& soinfo::get_children() { + if (has_min_version(0)) { + return children_; + } + + return g_empty_list; +} + +const soinfo_list_t& soinfo::get_children() const { + if (has_min_version(0)) { + return children_; + } + + return g_empty_list; +} + +soinfo_list_t& soinfo::get_parents() { + if (has_min_version(0)) { + return parents_; + } + + return g_empty_list; +} + +static std::vector<std::string> g_empty_runpath; + +const std::vector<std::string>& soinfo::get_dt_runpath() const { + if (has_min_version(3)) { + return dt_runpath_; + } + + return g_empty_runpath; +} + +android_namespace_t* soinfo::get_primary_namespace() { + if (has_min_version(3)) { + return primary_namespace_; + } + + return &g_default_namespace; +} + +void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) { + CHECK(has_min_version(3)); + secondary_namespaces_.push_back(secondary_ns); +} + +ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { + return call_ifunc_resolver(s->st_value + load_bias); + } + + return static_cast<ElfW(Addr)>(s->st_value + load_bias); +} + +const char* soinfo::get_string(ElfW(Word) index) const { + if (has_min_version(1) && (index >= strtab_size_)) { + __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", + get_realpath(), strtab_size_, index); + } + + return strtab_ + index; +} + +bool soinfo::is_gnu_hash() const { + return (flags_ & FLAG_GNU_HASH) != 0; +} + +bool soinfo::can_unload() const { + return !is_linked() || ((get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0); +} + +bool soinfo::is_linked() const { + return (flags_ & FLAG_LINKED) != 0; +} + +bool soinfo::is_main_executable() const { + return (flags_ & FLAG_EXE) != 0; +} + +bool soinfo::is_linker() const { + return (flags_ & FLAG_LINKER) != 0; +} + +void soinfo::set_linked() { + flags_ |= FLAG_LINKED; +} + +void soinfo::set_linker_flag() { + flags_ |= FLAG_LINKER; +} + +void soinfo::set_main_executable() { + flags_ |= FLAG_EXE; +} + +void soinfo::increment_ref_count() { + local_group_root_->ref_count_++; +} + +size_t soinfo::decrement_ref_count() { + return --local_group_root_->ref_count_; +} + +soinfo* soinfo::get_local_group_root() const { + return local_group_root_; +} + + +void soinfo::set_mapped_by_caller(bool mapped_by_caller) { + if (mapped_by_caller) { + flags_ |= FLAG_MAPPED_BY_CALLER; + } else { + flags_ &= ~FLAG_MAPPED_BY_CALLER; + } +} + +bool soinfo::is_mapped_by_caller() const { + return (flags_ & FLAG_MAPPED_BY_CALLER) != 0; +} + +// This function returns api-level at the time of +// dlopen/load. Note that libraries opened by system +// will always have 'current' api level. +uint32_t soinfo::get_target_sdk_version() const { + if (!has_min_version(2)) { + return __ANDROID_API__; + } + + return local_group_root_->target_sdk_version_; +} + +uintptr_t soinfo::get_handle() const { + CHECK(has_min_version(3)); + CHECK(handle_ != 0); + return handle_; +} + +void* soinfo::to_handle() { + if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) { + return this; + } + + return reinterpret_cast<void*>(get_handle()); +} + +void soinfo::generate_handle() { + CHECK(has_min_version(3)); + CHECK(handle_ == 0); // Make sure this is the first call + + // Make sure the handle is unique and does not collide + // with special values which are RTLD_DEFAULT and RTLD_NEXT. + do { + arc4random_buf(&handle_, sizeof(handle_)); + // the least significant bit for the handle is always 1 + // making it easy to test the type of handle passed to + // dl* functions. + handle_ = handle_ | 1; + } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) || + handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) || + g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end()); + + g_soinfo_handles_map[handle_] = this; +} + +// TODO(dimitry): Move SymbolName methods to a separate file. + +uint32_t calculate_elf_hash(const char* name) { + const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name); + uint32_t h = 0, g; + + while (*name_bytes) { + h = (h << 4) + *name_bytes++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + + return h; +} + +uint32_t SymbolName::elf_hash() { + if (!has_elf_hash_) { + elf_hash_ = calculate_elf_hash(name_); + has_elf_hash_ = true; + } + + return elf_hash_; +} + +uint32_t SymbolName::gnu_hash() { + if (!has_gnu_hash_) { + uint32_t h = 5381; + const uint8_t* name = reinterpret_cast<const uint8_t*>(name_); + while (*name != 0) { + h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c + } + + gnu_hash_ = h; + has_gnu_hash_ = true; + } + + return gnu_hash_; +} + +
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h new file mode 100644 index 0000000..d7b584e --- /dev/null +++ b/linker/linker_soinfo.h
@@ -0,0 +1,345 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __LINKER_SOINFO_H +#define __LINKER_SOINFO_H + +#include <link.h> + +#include <string> + +#include "linker_namespaces.h" + +#define FLAG_LINKED 0x00000001 +#define FLAG_EXE 0x00000004 // The main executable +#define FLAG_LINKER 0x00000010 // The linker itself +#define FLAG_GNU_HASH 0x00000040 // uses gnu hash +#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller + // and should not be unmapped +#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format + +#define SOINFO_VERSION 3 + +typedef void (*linker_dtor_function_t)(); +typedef void (*linker_ctor_function_t)(int, char**, char**); + +class SymbolName { + public: + explicit SymbolName(const char* name) + : name_(name), has_elf_hash_(false), has_gnu_hash_(false), + elf_hash_(0), gnu_hash_(0) { } + + const char* get_name() { + return name_; + } + + uint32_t elf_hash(); + uint32_t gnu_hash(); + + private: + const char* name_; + bool has_elf_hash_; + bool has_gnu_hash_; + uint32_t elf_hash_; + uint32_t gnu_hash_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName); +}; + +struct version_info { + constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {} + + uint32_t elf_hash; + const char* name; + const soinfo* target_si; +}; + +// TODO(dimitry): remove reference from soinfo member functions to this class. +class VersionTracker; + +#if defined(__work_around_b_24465209__) +#define SOINFO_NAME_LEN 128 +#endif + +struct soinfo { +#if defined(__work_around_b_24465209__) + private: + char old_name_[SOINFO_NAME_LEN]; +#endif + public: + const ElfW(Phdr)* phdr; + size_t phnum; +#if defined(__work_around_b_24465209__) + ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility. +#endif + ElfW(Addr) base; + size_t size; + +#if defined(__work_around_b_24465209__) + uint32_t unused1; // DO NOT USE, maintained for compatibility. +#endif + + ElfW(Dyn)* dynamic; + +#if defined(__work_around_b_24465209__) + uint32_t unused2; // DO NOT USE, maintained for compatibility + uint32_t unused3; // DO NOT USE, maintained for compatibility +#endif + + soinfo* next; + private: + uint32_t flags_; + + const char* strtab_; + ElfW(Sym)* symtab_; + + size_t nbucket_; + size_t nchain_; + uint32_t* bucket_; + uint32_t* chain_; + +#if defined(__mips__) || !defined(__LP64__) + // This is only used by mips and mips64, but needs to be here for + // all 32-bit architectures to preserve binary compatibility. + ElfW(Addr)** plt_got_; +#endif + +#if defined(USE_RELA) + ElfW(Rela)* plt_rela_; + size_t plt_rela_count_; + + ElfW(Rela)* rela_; + size_t rela_count_; +#else + ElfW(Rel)* plt_rel_; + size_t plt_rel_count_; + + ElfW(Rel)* rel_; + size_t rel_count_; +#endif + + linker_ctor_function_t* preinit_array_; + size_t preinit_array_count_; + + linker_ctor_function_t* init_array_; + size_t init_array_count_; + linker_dtor_function_t* fini_array_; + size_t fini_array_count_; + + linker_ctor_function_t init_func_; + linker_dtor_function_t fini_func_; + +#if defined(__arm__) + public: + // ARM EABI section used for stack unwinding. + uint32_t* ARM_exidx; + size_t ARM_exidx_count; + private: +#elif defined(__mips__) + uint32_t mips_symtabno_; + uint32_t mips_local_gotno_; + uint32_t mips_gotsym_; + bool mips_relocate_got(const VersionTracker& version_tracker, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); +#if !defined(__LP64__) + bool mips_check_and_adjust_fp_modes(); +#endif +#endif + size_t ref_count_; + public: + link_map link_map_head; + + bool constructors_called; + + // When you read a virtual address from the ELF file, add this + // value to get the corresponding address in the process' address space. + ElfW(Addr) load_bias; + +#if !defined(__LP64__) + bool has_text_relocations; +#endif + bool has_DT_SYMBOLIC; + + public: + soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat, + off64_t file_offset, int rtld_flags); + ~soinfo(); + + void call_constructors(); + void call_destructors(); + void call_pre_init_constructors(); + bool prelink_image(); + bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, + const android_dlextinfo* extinfo); + bool protect_relro(); + + void add_child(soinfo* child); + void remove_all_links(); + + ino_t get_st_ino() const; + dev_t get_st_dev() const; + off64_t get_file_offset() const; + + uint32_t get_rtld_flags() const; + uint32_t get_dt_flags_1() const; + void set_dt_flags_1(uint32_t dt_flags_1); + + soinfo_list_t& get_children(); + const soinfo_list_t& get_children() const; + + soinfo_list_t& get_parents(); + + bool find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi, + const ElfW(Sym)** symbol) const; + + ElfW(Sym)* find_symbol_by_address(const void* addr); + ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const; + + const char* get_string(ElfW(Word) index) const; + bool can_unload() const; + bool is_gnu_hash() const; + + bool inline has_min_version(uint32_t min_version __unused) const { +#if defined(__work_around_b_24465209__) + return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version; +#else + return true; +#endif + } + + bool is_linked() const; + bool is_linker() const; + bool is_main_executable() const; + + void set_linked(); + void set_linker_flag(); + void set_main_executable(); + void set_nodelete(); + + void increment_ref_count(); + size_t decrement_ref_count(); + + soinfo* get_local_group_root() const; + + void set_soname(const char* soname); + const char* get_soname() const; + const char* get_realpath() const; + const ElfW(Versym)* get_versym(size_t n) const; + ElfW(Addr) get_verneed_ptr() const; + size_t get_verneed_cnt() const; + ElfW(Addr) get_verdef_ptr() const; + size_t get_verdef_cnt() const; + + uint32_t get_target_sdk_version() const; + + void set_dt_runpath(const char *); + const std::vector<std::string>& get_dt_runpath() const; + android_namespace_t* get_primary_namespace(); + void add_secondary_namespace(android_namespace_t* secondary_ns); + + void set_mapped_by_caller(bool reserved_map); + bool is_mapped_by_caller() const; + + uintptr_t get_handle() const; + void generate_handle(); + void* to_handle(); + + private: + bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; + ElfW(Sym)* elf_addr_lookup(const void* addr); + bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; + ElfW(Sym)* gnu_addr_lookup(const void* addr); + + bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, + const char* sym_name, const version_info** vi); + + template<typename ElfRelIteratorT> + bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, + const soinfo_list_t& global_group, const soinfo_list_t& local_group); + + private: + // This part of the structure is only available + // when FLAG_NEW_SOINFO is set in this->flags. + uint32_t version_; + + // version >= 0 + dev_t st_dev_; + ino_t st_ino_; + + // dependency graph + soinfo_list_t children_; + soinfo_list_t parents_; + + // version >= 1 + off64_t file_offset_; + uint32_t rtld_flags_; + uint32_t dt_flags_1_; + size_t strtab_size_; + + // version >= 2 + + size_t gnu_nbucket_; + uint32_t* gnu_bucket_; + uint32_t* gnu_chain_; + uint32_t gnu_maskwords_; + uint32_t gnu_shift2_; + ElfW(Addr)* gnu_bloom_filter_; + + soinfo* local_group_root_; + + uint8_t* android_relocs_; + size_t android_relocs_size_; + + const char* soname_; + std::string realpath_; + + const ElfW(Versym)* versym_; + + ElfW(Addr) verdef_ptr_; + size_t verdef_cnt_; + + ElfW(Addr) verneed_ptr_; + size_t verneed_cnt_; + + uint32_t target_sdk_version_; + + // version >= 3 + std::vector<std::string> dt_runpath_; + android_namespace_t* primary_namespace_; + android_namespace_list_t secondary_namespaces_; + uintptr_t handle_; + + friend soinfo* get_libdl_info(); +}; + +// This function is used by dlvsym() to calculate hash of sym_ver +uint32_t calculate_elf_hash(const char* name); + +#endif /* __LINKER_SOINFO_H */
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp new file mode 100644 index 0000000..e7447e4 --- /dev/null +++ b/linker/linker_utils.cpp
@@ -0,0 +1,204 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_utils.h" + +#include "linker_debug.h" +#include "linker_globals.h" + +#include "android-base/strings.h" + +#include <sys/stat.h> +#include <unistd.h> + +std::string dirname(const char* path) { + const char* last_slash = strrchr(path, '/'); + + if (last_slash == path) { + return "/"; + } else if (last_slash == nullptr) { + return "."; + } else { + return std::string(path, last_slash - path); + } +} + +bool normalize_path(const char* path, std::string* normalized_path) { + // Input should be an absolute path + if (path[0] != '/') { + PRINT("normalize_path - invalid input: \"%s\", the input path should be absolute", path); + return false; + } + + const size_t len = strlen(path) + 1; + char buf[len]; + + const char* in_ptr = path; + char* out_ptr = buf; + + while (*in_ptr != 0) { + if (*in_ptr == '/') { + char c1 = in_ptr[1]; + if (c1 == '.') { + char c2 = in_ptr[2]; + if (c2 == '/') { + in_ptr += 2; + continue; + } else if (c2 == '.' && (in_ptr[3] == '/' || in_ptr[3] == 0)) { + in_ptr += 3; + while (out_ptr > buf && *--out_ptr != '/') { + } + if (in_ptr[0] == 0) { + // retain '/' + out_ptr++; + } + continue; + } + } else if (c1 == '/') { + ++in_ptr; + continue; + } + } + *out_ptr++ = *in_ptr++; + } + + *out_ptr = 0; + *normalized_path = buf; + return true; +} + +bool file_is_in_dir(const std::string& file, const std::string& dir) { + const char* needle = dir.c_str(); + const char* haystack = file.c_str(); + size_t needle_len = strlen(needle); + + return strncmp(haystack, needle, needle_len) == 0 && + haystack[needle_len] == '/' && + strchr(haystack + needle_len + 1, '/') == nullptr; +} + +bool file_is_under_dir(const std::string& file, const std::string& dir) { + const char* needle = dir.c_str(); + const char* haystack = file.c_str(); + size_t needle_len = strlen(needle); + + return strncmp(haystack, needle, needle_len) == 0 && + haystack[needle_len] == '/'; +} + +const char* const kZipFileSeparator = "!/"; + +bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path) { + std::string normalized_path; + if (!normalize_path(input_path, &normalized_path)) { + return false; + } + + const char* const path = normalized_path.c_str(); + TRACE("Trying zip file open from path \"%s\" -> normalized \"%s\"", input_path, path); + + // Treat an '!/' separator inside a path as the separator between the name + // of the zip file on disk and the subdirectory to search within it. + // For example, if path is "foo.zip!/bar/bas/x.so", then we search for + // "bar/bas/x.so" within "foo.zip". + const char* const separator = strstr(path, kZipFileSeparator); + if (separator == nullptr) { + return false; + } + + char buf[512]; + if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) { + PRINT("Warning: ignoring very long library path: %s", path); + return false; + } + + buf[separator - path] = '\0'; + + *zip_path = buf; + *entry_path = &buf[separator - path + 2]; + + return true; +} + +constexpr off64_t kPageMask = ~static_cast<off64_t>(PAGE_SIZE-1); + +off64_t page_start(off64_t offset) { + return offset & kPageMask; +} + +bool safe_add(off64_t* out, off64_t a, size_t b) { + CHECK(a >= 0); + if (static_cast<uint64_t>(INT64_MAX - a) < b) { + return false; + } + + *out = a + b; + return true; +} + +size_t page_offset(off64_t offset) { + return static_cast<size_t>(offset & (PAGE_SIZE-1)); +} + +void split_path(const char* path, const char* delimiters, + std::vector<std::string>* paths) { + if (path != nullptr && path[0] != 0) { + *paths = android::base::Split(path, delimiters); + } +} + +void resolve_paths(std::vector<std::string>& paths, + std::vector<std::string>* resolved_paths) { + resolved_paths->clear(); + for (const auto& path : paths) { + char resolved_path[PATH_MAX]; + const char* original_path = path.c_str(); + if (realpath(original_path, resolved_path) != nullptr) { + struct stat s; + if (stat(resolved_path, &s) == 0) { + if (S_ISDIR(s.st_mode)) { + resolved_paths->push_back(resolved_path); + } else { + DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path); + continue; + } + } else { + DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno)); + continue; + } + } else { + std::string zip_path; + std::string entry_path; + + std::string normalized_path; + + if (!normalize_path(original_path, &normalized_path)) { + DL_WARN("Warning: unable to normalize \"%s\"", original_path); + continue; + } + + if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) { + if (realpath(zip_path.c_str(), resolved_path) == nullptr) { + DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno)); + continue; + } + + resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path); + } + } + } +} +
diff --git a/linker/linker_utils.h b/linker/linker_utils.h new file mode 100644 index 0000000..2e015a5 --- /dev/null +++ b/linker/linker_utils.h
@@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __LINKER_UTILS_H +#define __LINKER_UTILS_H + +#include <string> +#include <vector> + +extern const char* const kZipFileSeparator; + +bool file_is_in_dir(const std::string& file, const std::string& dir); +bool file_is_under_dir(const std::string& file, const std::string& dir); +bool normalize_path(const char* path, std::string* normalized_path); +bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path); + +// For every path element this function checks of it exists, and is a directory, +// and normalizes it: +// 1. For regular path it converts it to realpath() +// 2. For path in a zip file it uses realpath on the zipfile +// normalizes entry name by calling normalize_path function. +void resolve_paths(std::vector<std::string>& paths, std::vector<std::string>* resolved_paths); +void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths); + +std::string dirname(const char* path); + +off64_t page_start(off64_t offset); +size_t page_offset(off64_t offset); +bool safe_add(off64_t* out, off64_t a, size_t b); + +#endif
diff --git a/linker/rt.cpp b/linker/rt.cpp new file mode 100644 index 0000000..710892a --- /dev/null +++ b/linker/rt.cpp
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This function is an empty stub where GDB locates a breakpoint to get notified + * about linker activity. It canʼt be inlined away, can't be hidden. + */ +extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity() { +} +
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk new file mode 100644 index 0000000..d5b57f1 --- /dev/null +++ b/linker/tests/Android.mk
@@ -0,0 +1,45 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := linker-unit-tests +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +LOCAL_CFLAGS += -g -Wall -Wextra -Wunused -Werror +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/ + +LOCAL_SRC_FILES := \ + linker_block_allocator_test.cpp \ + linker_globals.cpp \ + linked_list_test.cpp \ + linker_memory_allocator_test.cpp \ + linker_sleb128_test.cpp \ + linker_utils_test.cpp \ + ../linker_allocator.cpp \ + ../linker_block_allocator.cpp \ + ../linker_utils.cpp + +# for __libc_fatal +LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp + +LOCAL_STATIC_LIBRARIES += libbase + +include $(BUILD_NATIVE_TEST)
diff --git a/linker/tests/linked_list_test.cpp b/linker/tests/linked_list_test.cpp new file mode 100644 index 0000000..12348d9 --- /dev/null +++ b/linker/tests/linked_list_test.cpp
@@ -0,0 +1,233 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string> +#include <sstream> + +#include <gtest/gtest.h> + +#include "../linked_list.h" + +namespace { + +bool alloc_called = false; +bool free_called = false; + +class LinkedListTestAllocator { + public: + typedef LinkedListEntry<const char> entry_t; + + static entry_t* alloc() { + alloc_called = true; + return reinterpret_cast<entry_t*>(::malloc(sizeof(entry_t))); + } + + static void free(entry_t* p) { + free_called = true; + ::free(p); + } + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(LinkedListTestAllocator); +}; + +typedef LinkedList<const char, LinkedListTestAllocator> test_list_t; + +std::string test_list_to_string(test_list_t& list) { + std::stringstream ss; + list.for_each([&] (const char* c) { + ss << c; + }); + + return ss.str(); +} + +}; + +TEST(linked_list, simple) { + alloc_called = free_called = false; + test_list_t list; + ASSERT_EQ("", test_list_to_string(list)); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(!free_called); + list.push_front("a"); + ASSERT_TRUE(alloc_called); + ASSERT_TRUE(!free_called); + ASSERT_EQ("a", test_list_to_string(list)); + list.push_front("b"); + ASSERT_EQ("ba", test_list_to_string(list)); + list.push_front("c"); + list.push_front("d"); + ASSERT_EQ("dcba", test_list_to_string(list)); + ASSERT_TRUE(alloc_called); + ASSERT_TRUE(!free_called); + alloc_called = free_called = false; + list.remove_if([] (const char* c) { + return *c == 'c'; + }); + + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(free_called); + + ASSERT_EQ("dba", test_list_to_string(list)); + alloc_called = free_called = false; + list.remove_if([] (const char* c) { + return *c == '2'; + }); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(!free_called); + ASSERT_EQ("dba", test_list_to_string(list)); + list.clear(); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(free_called); + ASSERT_EQ("", test_list_to_string(list)); +} + +TEST(linked_list, push_pop) { + test_list_t list; + list.push_front("b"); + list.push_front("a"); + ASSERT_EQ("ab", test_list_to_string(list)); + list.push_back("c"); + ASSERT_EQ("abc", test_list_to_string(list)); + ASSERT_STREQ("a", list.pop_front()); + ASSERT_EQ("bc", test_list_to_string(list)); + ASSERT_STREQ("b", list.pop_front()); + ASSERT_EQ("c", test_list_to_string(list)); + ASSERT_STREQ("c", list.pop_front()); + ASSERT_EQ("", test_list_to_string(list)); + ASSERT_TRUE(list.pop_front() == nullptr); + list.push_back("r"); + ASSERT_EQ("r", test_list_to_string(list)); + ASSERT_STREQ("r", list.pop_front()); + ASSERT_TRUE(list.pop_front() == nullptr); +} + +TEST(linked_list, remove_if_then_pop) { + test_list_t list; + list.push_back("a"); + list.push_back("b"); + list.push_back("c"); + list.push_back("d"); + list.remove_if([](const char* c) { + return *c == 'b' || *c == 'c'; + }); + + ASSERT_EQ("ad", test_list_to_string(list)); + ASSERT_STREQ("a", list.pop_front()); + ASSERT_EQ("d", test_list_to_string(list)); + ASSERT_STREQ("d", list.pop_front()); + ASSERT_TRUE(list.pop_front() == nullptr); +} + +TEST(linked_list, remove_if_last_then_push_back) { + test_list_t list; + + list.push_back("a"); + list.push_back("b"); + list.push_back("c"); + list.push_back("d"); + + list.remove_if([](const char* c) { + return *c == 'c' || *c == 'd'; + }); + + ASSERT_EQ("ab", test_list_to_string(list)); + list.push_back("d"); + ASSERT_EQ("abd", test_list_to_string(list)); +} + +TEST(linked_list, copy_to_array) { + test_list_t list; + const size_t max_size = 128; + const char* buf[max_size]; + memset(buf, 0, sizeof(buf)); + + ASSERT_EQ(0U, list.copy_to_array(buf, max_size)); + ASSERT_EQ(nullptr, buf[0]); + + list.push_back("a"); + list.push_back("b"); + list.push_back("c"); + list.push_back("d"); + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(2U, list.copy_to_array(buf, 2)); + ASSERT_STREQ("a", buf[0]); + ASSERT_STREQ("b", buf[1]); + ASSERT_EQ(nullptr, buf[2]); + + ASSERT_EQ(4U, list.copy_to_array(buf, max_size)); + ASSERT_STREQ("a", buf[0]); + ASSERT_STREQ("b", buf[1]); + ASSERT_STREQ("c", buf[2]); + ASSERT_STREQ("d", buf[3]); + ASSERT_EQ(nullptr, buf[4]); + + memset(buf, 0, sizeof(buf)); + list.remove_if([](const char* c) { + return *c != 'c'; + }); + ASSERT_EQ(1U, list.copy_to_array(buf, max_size)); + ASSERT_STREQ("c", buf[0]); + ASSERT_EQ(nullptr, buf[1]); + + memset(buf, 0, sizeof(buf)); + + list.remove_if([](const char* c) { + return *c == 'c'; + }); + + ASSERT_EQ(0U, list.copy_to_array(buf, max_size)); + ASSERT_EQ(nullptr, buf[0]); +} + +TEST(linked_list, test_visit) { + test_list_t list; + list.push_back("a"); + list.push_back("b"); + list.push_back("c"); + list.push_back("d"); + + int visits = 0; + std::stringstream ss; + bool result = list.visit([&](const char* c) { + ++visits; + ss << c; + return true; + }); + + ASSERT_TRUE(result); + ASSERT_EQ(4, visits); + ASSERT_EQ("abcd", ss.str()); + + visits = 0; + ss.str(std::string()); + + result = list.visit([&](const char* c) { + if (++visits == 3) { + return false; + } + + ss << c; + return true; + }); + + ASSERT_TRUE(!result); + ASSERT_EQ(3, visits); + ASSERT_EQ("ab", ss.str()); +} +
diff --git a/linker/tests/linker_block_allocator_test.cpp b/linker/tests/linker_block_allocator_test.cpp new file mode 100644 index 0000000..5adc425 --- /dev/null +++ b/linker/tests/linker_block_allocator_test.cpp
@@ -0,0 +1,136 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include "../linker_block_allocator.h" + +#include <unistd.h> + +namespace { + +struct test_struct_nominal { + void* pointer; + ssize_t value; +}; + +/* + * this one has size below allocator cap which is 2*sizeof(void*) + */ +struct test_struct_small { + char dummy_str[5]; +}; + +/* + * 1009 byte struct (1009 is prime) + */ +struct test_struct_larger { + char dummy_str[1009]; +}; + +static size_t kPageSize = sysconf(_SC_PAGE_SIZE); +}; + +TEST(linker_allocator, test_nominal) { + LinkerTypeAllocator<test_struct_nominal> allocator; + + test_struct_nominal* ptr1 = allocator.alloc(); + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + test_struct_nominal* ptr2 = allocator.alloc(); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + ASSERT_TRUE(ptr2 != nullptr); + // they should be next to each other. + ASSERT_EQ(reinterpret_cast<uint8_t*>(ptr1)+16, reinterpret_cast<uint8_t*>(ptr2)); + + ptr1->value = 42; + + allocator.free(ptr1); + allocator.free(ptr2); +} + +TEST(linker_allocator, test_small) { + LinkerTypeAllocator<test_struct_small> allocator; + + char* ptr1 = reinterpret_cast<char*>(allocator.alloc()); + char* ptr2 = reinterpret_cast<char*>(allocator.alloc()); + + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + ASSERT_TRUE(ptr2 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + ASSERT_EQ(ptr1+16, ptr2); // aligned to 16 +} + +TEST(linker_allocator, test_larger) { + LinkerTypeAllocator<test_struct_larger> allocator; + + test_struct_larger* ptr1 = allocator.alloc(); + test_struct_larger* ptr2 = allocator.alloc(); + + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + ASSERT_TRUE(ptr2 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + + ASSERT_EQ(reinterpret_cast<uint8_t*>(ptr1) + 1024, reinterpret_cast<uint8_t*>(ptr2)); + + // lets allocate until we reach next page. + size_t n = kPageSize/sizeof(test_struct_larger) + 1 - 2; + + for (size_t i=0; i<n; ++i) { + ASSERT_TRUE(allocator.alloc() != nullptr); + } + + test_struct_larger* ptr_to_free = allocator.alloc(); + ASSERT_TRUE(ptr_to_free != nullptr); + allocator.free(ptr1); +} + +static void protect_all() { + LinkerTypeAllocator<test_struct_larger> allocator; + + // number of allocs to reach the end of first page + size_t n = kPageSize/sizeof(test_struct_larger) - 1; + test_struct_larger* page1_ptr = allocator.alloc(); + + for (size_t i=0; i<n; ++i) { + allocator.alloc(); + } + + test_struct_larger* page2_ptr = allocator.alloc(); + allocator.protect_all(PROT_READ); + allocator.protect_all(PROT_READ | PROT_WRITE); + // check access + page2_ptr->dummy_str[23] = 27; + page1_ptr->dummy_str[13] = 11; + + allocator.protect_all(PROT_READ); + fprintf(stderr, "trying to access protected page"); + + // this should result in segmentation fault + page1_ptr->dummy_str[11] = 7; +} + +TEST(linker_allocator, test_protect) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + ASSERT_EXIT(protect_all(), testing::KilledBySignal(SIGSEGV), "trying to access protected page"); +} +
diff --git a/linker/tests/linker_globals.cpp b/linker/tests/linker_globals.cpp new file mode 100644 index 0000000..7762a87 --- /dev/null +++ b/linker/tests/linker_globals.cpp
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// To enable logging +int g_ld_debug_verbosity = 0; +
diff --git a/linker/tests/linker_memory_allocator_test.cpp b/linker/tests/linker_memory_allocator_test.cpp new file mode 100644 index 0000000..5b85536 --- /dev/null +++ b/linker/tests/linker_memory_allocator_test.cpp
@@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include "../linker_allocator.h" + +#include <unistd.h> + +namespace { + +/* + * this one has size below allocator cap which is 2*sizeof(void*) + */ +struct test_struct_small { + char dummy_str[5]; +}; + +struct test_struct_large { + char dummy_str[1009]; +}; + +struct test_struct_huge { + char dummy_str[73939]; +}; + +struct test_struct_512 { + char dummy_str[503]; +}; + +}; + +static size_t kPageSize = sysconf(_SC_PAGE_SIZE); + +TEST(linker_memory, test_alloc_0) { + LinkerMemoryAllocator allocator; + void* ptr = allocator.alloc(0); + ASSERT_TRUE(ptr != nullptr); + allocator.free(ptr); +} + +TEST(linker_memory, test_free_nullptr) { + LinkerMemoryAllocator allocator; + allocator.free(nullptr); +} + +TEST(linker_memory, test_realloc) { + LinkerMemoryAllocator allocator; + uint32_t* array = reinterpret_cast<uint32_t*>(allocator.alloc(512)); + const size_t array_size = 512 / sizeof(uint32_t); + + uint32_t model[1000]; + + model[0] = 1; + model[1] = 1; + + for (size_t i = 2; i < 1000; ++i) { + model[i] = model[i - 1] + model[i - 2]; + } + + memcpy(array, model, array_size); + + uint32_t* reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 1024)); + + ASSERT_TRUE(reallocated_ptr != nullptr); + ASSERT_TRUE(reallocated_ptr != array); + + ASSERT_TRUE(memcmp(reallocated_ptr, model, array_size) == 0); + + array = reallocated_ptr; + + memcpy(array, model, 2*array_size); + + reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 62)); + + ASSERT_TRUE(reallocated_ptr == array); + + reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 4000)); + + ASSERT_TRUE(reallocated_ptr != nullptr); + ASSERT_TRUE(reallocated_ptr != array); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(reallocated_ptr) % 16); + + ASSERT_TRUE(memcmp(reallocated_ptr, model, array_size * 2) == 0); + + array = reallocated_ptr; + + memcpy(array, model, 4000); + + reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 64000)); + + ASSERT_TRUE(reallocated_ptr != nullptr); + ASSERT_TRUE(reallocated_ptr != array); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(reallocated_ptr) % 16); + + ASSERT_TRUE(memcmp(reallocated_ptr, model, 4000) == 0); + + ASSERT_EQ(nullptr, allocator.realloc(reallocated_ptr, 0)); +} + +TEST(linker_memory, test_small_smoke) { + LinkerMemoryAllocator allocator; + + uint8_t zeros[16]; + memset(zeros, 0, sizeof(zeros)); + + test_struct_small* ptr1 = + reinterpret_cast<test_struct_small*>(allocator.alloc(sizeof(test_struct_small))); + test_struct_small* ptr2 = + reinterpret_cast<test_struct_small*>(allocator.alloc(sizeof(test_struct_small))); + + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + ASSERT_TRUE(ptr2 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + + ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr1)+16, reinterpret_cast<uintptr_t>(ptr2)); + ASSERT_TRUE(memcmp(ptr1, zeros, 16) == 0); + + allocator.free(ptr1); + allocator.free(ptr2); +} + +TEST(linker_memory, test_huge_smoke) { + LinkerMemoryAllocator allocator; + + // this should trigger proxy-to-mmap + test_struct_huge* ptr1 = + reinterpret_cast<test_struct_huge*>(allocator.alloc(sizeof(test_struct_huge))); + test_struct_huge* ptr2 = + reinterpret_cast<test_struct_huge*>(allocator.alloc(sizeof(test_struct_huge))); + + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + ASSERT_TRUE(ptr2 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + + ASSERT_TRUE( + reinterpret_cast<uintptr_t>(ptr1)/kPageSize != reinterpret_cast<uintptr_t>(ptr2)/kPageSize); + allocator.free(ptr2); + allocator.free(ptr1); +} + +TEST(linker_memory, test_large) { + LinkerMemoryAllocator allocator; + + test_struct_large* ptr1 = + reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large))); + test_struct_large* ptr2 = + reinterpret_cast<test_struct_large*>(allocator.alloc(1024)); + + ASSERT_TRUE(ptr1 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16); + ASSERT_TRUE(ptr2 != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16); + + ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr1) + 1024, reinterpret_cast<uintptr_t>(ptr2)); + + // let's allocate until we reach the next page. + size_t n = kPageSize / sizeof(test_struct_large) + 1 - 2; + test_struct_large* objects[n]; + + for (size_t i = 0; i < n; ++i) { + test_struct_large* obj_ptr = + reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large))); + ASSERT_TRUE(obj_ptr != nullptr); + objects[i] = obj_ptr; + } + + test_struct_large* ptr_to_free = + reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large))); + + ASSERT_TRUE(ptr_to_free != nullptr); + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr_to_free) % 16); + + allocator.free(ptr1); + + for (size_t i=0; i<n; ++i) { + allocator.free(objects[i]); + } + + allocator.free(ptr2); + allocator.free(ptr_to_free); +} + +
diff --git a/linker/tests/linker_sleb128_test.cpp b/linker/tests/linker_sleb128_test.cpp new file mode 100644 index 0000000..4e29bca --- /dev/null +++ b/linker/tests/linker_sleb128_test.cpp
@@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include "../linker_sleb128.h" + +TEST(linker_sleb128, smoke) { + std::vector<uint8_t> encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); +#if defined(__LP64__) + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); +#endif + sleb128_decoder decoder(&encoding[0], encoding.size()); + + EXPECT_EQ(624485U, decoder.pop_front()); + + EXPECT_EQ(0U, decoder.pop_front()); + EXPECT_EQ(1U, decoder.pop_front()); + EXPECT_EQ(63U, decoder.pop_front()); + EXPECT_EQ(64U, decoder.pop_front()); + EXPECT_EQ(static_cast<size_t>(-1), decoder.pop_front()); + EXPECT_EQ(static_cast<size_t>(-624485), decoder.pop_front()); + EXPECT_EQ(2147483647U, decoder.pop_front()); + EXPECT_EQ(static_cast<size_t>(-2147483648), decoder.pop_front()); +#if defined(__LP64__) + EXPECT_EQ(9223372036854775807ULL, decoder.pop_front()); + EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), decoder.pop_front()); +#endif +}
diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp new file mode 100644 index 0000000..fd749fa --- /dev/null +++ b/linker/tests/linker_utils_test.cpp
@@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include "../linker_utils.h" + +TEST(linker_utils, normalize_path_smoke) { + std::string output; + ASSERT_TRUE(normalize_path("/../root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output)); + ASSERT_EQ("/root/dir/dir2/zipfile!/dir/afile", output); + + ASSERT_TRUE(normalize_path("/../root///dir/.///dir2/somedir/.../zipfile!/.dir/dir9//..///afile", &output)); + ASSERT_EQ("/root/dir/dir2/somedir/.../zipfile!/.dir/afile", output); + + ASSERT_TRUE(normalize_path("/root/..", &output)); + ASSERT_EQ("/", output); + + ASSERT_TRUE(normalize_path("/root/notroot/..", &output)); + ASSERT_EQ("/root/", output); + + ASSERT_TRUE(normalize_path("/a/../../b", &output)); + ASSERT_EQ("/b", output); + + output = "unchanged"; + ASSERT_FALSE(normalize_path("root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output)); + ASSERT_EQ("unchanged", output); +} + +TEST(linker_utils, file_is_in_dir_smoke) { + ASSERT_TRUE(file_is_in_dir("/foo/bar/file", "/foo/bar")); + ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/foo")); + + ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/bar/foo")); + + ASSERT_TRUE(file_is_in_dir("/file", "")); + ASSERT_FALSE(file_is_in_dir("/file", "/")); +} + +TEST(linker_utils, file_is_under_dir_smoke) { + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "/foo/bar")); + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "/foo")); + + ASSERT_FALSE(file_is_under_dir("/foo/bar/file", "/bar/foo")); + + ASSERT_TRUE(file_is_under_dir("/file", "")); + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "")); + ASSERT_FALSE(file_is_under_dir("/file", "/")); + ASSERT_FALSE(file_is_under_dir("/foo/bar/file", "/")); +} + +TEST(linker_utils, parse_zip_path_smoke) { + std::string zip_path; + std::string entry_path; + + ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip", &zip_path, &entry_path)); + ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip!path/in/zip", &zip_path, &entry_path)); + ASSERT_TRUE(parse_zip_path("/zip/path/file.zip!/path/in/zip", &zip_path, &entry_path)); + ASSERT_EQ("/zip/path/file.zip", zip_path); + ASSERT_EQ("path/in/zip", entry_path); + + ASSERT_TRUE(parse_zip_path("/zip/path/file2.zip!/", &zip_path, &entry_path)); + ASSERT_EQ("/zip/path/file2.zip", zip_path); + ASSERT_EQ("", entry_path); +} + +TEST(linker_utils, page_start) { + ASSERT_EQ(0x0001000, page_start(0x0001000)); + ASSERT_EQ(0x3002000, page_start(0x300222f)); + ASSERT_EQ(0x6001000, page_start(0x6001fff)); +} + +TEST(linker_utils, page_offset) { + ASSERT_EQ(0x0U, page_offset(0x0001000)); + ASSERT_EQ(0x22fU, page_offset(0x300222f)); + ASSERT_EQ(0xfffU, page_offset(0x6001fff)); +} + +TEST(linker_utils, safe_add) { + int64_t val = 42; + ASSERT_FALSE(safe_add(&val, INT64_MAX-20, 21U)); + ASSERT_EQ(42, val); + ASSERT_TRUE(safe_add(&val, INT64_MAX-42, 42U)); + ASSERT_EQ(INT64_MAX, val); + ASSERT_TRUE(safe_add(&val, 2000, 42U)); + ASSERT_EQ(2042, val); +}