Merge "Loosen fchmodat AT_SYMLINK_NOFOLLOW test on symlink."
diff --git a/libc/include/elf.h b/libc/include/elf.h
index a41a244..41680a5 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -94,6 +94,13 @@
#define DT_PREINIT_ARRAY 32
#define DT_PREINIT_ARRAYSZ 33
+/* Android compressed rel/rela sections */
+#define DT_ANDROID_REL (DT_LOOS + 2)
+#define DT_ANDROID_RELSZ (DT_LOOS + 3)
+
+#define DT_ANDROID_RELA (DT_LOOS + 4)
+#define DT_ANDROID_RELASZ (DT_LOOS + 5)
+
/* gnu hash entry */
#define DT_GNU_HASH 0x6ffffef5
diff --git a/libm/Android.mk b/libm/Android.mk
index 15c390c..418ec59 100644
--- a/libm/Android.mk
+++ b/libm/Android.mk
@@ -331,45 +331,73 @@
# -----------------------------------------------------------------------------
LOCAL_SRC_FILES_x86 += \
i387/fenv.c \
- upstream-freebsd/lib/msun/src/e_sqrt.c \
- upstream-freebsd/lib/msun/src/e_sqrtf.c \
- upstream-freebsd/lib/msun/src/s_ceil.c \
- upstream-freebsd/lib/msun/src/s_ceilf.c \
upstream-freebsd/lib/msun/src/s_fma.c \
upstream-freebsd/lib/msun/src/s_fmaf.c \
- upstream-freebsd/lib/msun/src/s_floor.c \
- upstream-freebsd/lib/msun/src/s_floorf.c \
upstream-freebsd/lib/msun/src/s_llrint.c \
upstream-freebsd/lib/msun/src/s_llrintf.c \
upstream-freebsd/lib/msun/src/s_lrint.c \
upstream-freebsd/lib/msun/src/s_lrintf.c \
upstream-freebsd/lib/msun/src/s_rint.c \
upstream-freebsd/lib/msun/src/s_rintf.c \
+ x86/sqrt.S \
+ x86/sqrtf.S \
+
+ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
+LOCAL_SRC_FILES_x86 += \
+ x86/ceil.S \
+ x86/ceilf.S \
+ x86/floor.S \
+ x86/floorf.S \
+ x86/trunc.S \
+ x86/truncf.S \
+
+else
+LOCAL_SRC_FILES_x86 += \
+ upstream-freebsd/lib/msun/src/s_ceil.c \
+ upstream-freebsd/lib/msun/src/s_ceilf.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
+ upstream-freebsd/lib/msun/src/s_floorf.c \
upstream-freebsd/lib/msun/src/s_trunc.c \
upstream-freebsd/lib/msun/src/s_truncf.c \
+endif
+
# -----------------------------------------------------------------------------
# x86_64
# -----------------------------------------------------------------------------
LOCAL_SRC_FILES_x86_64 += \
amd64/fenv.c \
- upstream-freebsd/lib/msun/src/e_sqrt.c \
- upstream-freebsd/lib/msun/src/e_sqrtf.c \
- upstream-freebsd/lib/msun/src/s_ceil.c \
- upstream-freebsd/lib/msun/src/s_ceilf.c \
upstream-freebsd/lib/msun/src/s_fma.c \
upstream-freebsd/lib/msun/src/s_fmaf.c \
- upstream-freebsd/lib/msun/src/s_floor.c \
- upstream-freebsd/lib/msun/src/s_floorf.c \
upstream-freebsd/lib/msun/src/s_llrint.c \
upstream-freebsd/lib/msun/src/s_llrintf.c \
upstream-freebsd/lib/msun/src/s_lrint.c \
upstream-freebsd/lib/msun/src/s_lrintf.c \
upstream-freebsd/lib/msun/src/s_rint.c \
upstream-freebsd/lib/msun/src/s_rintf.c \
+ x86_64/sqrt.S \
+ x86_64/sqrtf.S \
+
+ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
+LOCAL_SRC_FILES_x86_64 += \
+ x86_64/ceil.S \
+ x86_64/ceilf.S \
+ x86_64/floor.S \
+ x86_64/floorf.S \
+ x86_64/trunc.S \
+ x86_64/truncf.S \
+
+else
+LOCAL_SRC_FILES_x86_64 += \
+ upstream-freebsd/lib/msun/src/s_ceil.c \
+ upstream-freebsd/lib/msun/src/s_ceilf.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
+ upstream-freebsd/lib/msun/src/s_floorf.c \
upstream-freebsd/lib/msun/src/s_trunc.c \
upstream-freebsd/lib/msun/src/s_truncf.c \
+endif
+
LOCAL_C_INCLUDES_x86 += $(LOCAL_PATH)/i387
LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-freebsd/lib/msun/src/
diff --git a/libm/x86/ceil.S b/libm/x86/ceil.S
new file mode 100644
index 0000000..b992057
--- /dev/null
+++ b/libm/x86/ceil.S
@@ -0,0 +1,44 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(ceil)
+ mov %esp,%eax
+ and $0xfffffff8,%eax
+ movsd 0x4(%esp),%xmm0
+ roundsd $0x2,%xmm0,%xmm0
+ movlpd %xmm0,-0x8(%eax)
+ fldl -0x8(%eax)
+ ret
+END(ceil)
+
+.globl ceill;
+.equ ceill, ceil;
diff --git a/libm/x86/ceilf.S b/libm/x86/ceilf.S
new file mode 100644
index 0000000..51eb440
--- /dev/null
+++ b/libm/x86/ceilf.S
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(ceilf)
+ movss 0x4(%esp),%xmm0
+ roundss $0x2,%xmm0,%xmm0
+ movss %xmm0,-0x4(%esp)
+ flds -0x4(%esp)
+ ret
+END(ceilf)
diff --git a/libm/x86/floor.S b/libm/x86/floor.S
new file mode 100644
index 0000000..2b815f3
--- /dev/null
+++ b/libm/x86/floor.S
@@ -0,0 +1,44 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(floor)
+ mov %esp,%eax
+ and $0xfffffff8,%eax
+ movsd 0x4(%esp),%xmm0
+ roundsd $0x1,%xmm0,%xmm0
+ movlpd %xmm0,-0x8(%eax)
+ fldl -0x8(%eax)
+ ret
+END(floor)
+
+.globl floorl;
+.equ floorl, floor;
diff --git a/libm/x86/floorf.S b/libm/x86/floorf.S
new file mode 100644
index 0000000..79b9073
--- /dev/null
+++ b/libm/x86/floorf.S
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(floorf)
+ movss 0x4(%esp),%xmm0
+ roundss $0x1,%xmm0,%xmm0
+ movss %xmm0,-0x4(%esp)
+ flds -0x4(%esp)
+ ret
+END(floorf)
diff --git a/libm/x86/sqrt.S b/libm/x86/sqrt.S
new file mode 100644
index 0000000..3e459ce
--- /dev/null
+++ b/libm/x86/sqrt.S
@@ -0,0 +1,44 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(sqrt)
+ mov %esp,%eax
+ and $0xfffffff8,%eax
+ movsd 0x4(%esp),%xmm0
+ sqrtsd %xmm0,%xmm0
+ movlpd %xmm0,-0x8(%eax)
+ fldl -0x8(%eax)
+ ret
+END(sqrt)
+
+.globl sqrtl;
+.equ sqrtl, sqrt;
diff --git a/libm/x86/sqrtf.S b/libm/x86/sqrtf.S
new file mode 100644
index 0000000..78c183b
--- /dev/null
+++ b/libm/x86/sqrtf.S
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(sqrtf)
+ movss 0x4(%esp),%xmm0
+ sqrtss %xmm0,%xmm0
+ movss %xmm0,-0x4(%esp)
+ flds -0x4(%esp)
+ ret
+END(sqrtf)
diff --git a/libm/x86/trunc.S b/libm/x86/trunc.S
new file mode 100644
index 0000000..2f21e88
--- /dev/null
+++ b/libm/x86/trunc.S
@@ -0,0 +1,44 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(trunc)
+ mov %esp,%eax
+ and $0xfffffff8,%eax
+ movsd 0x4(%esp),%xmm0
+ roundsd $0x3,%xmm0,%xmm0
+ movlpd %xmm0,-0x8(%eax)
+ fldl -0x8(%eax)
+ ret
+END(trunc)
+
+.globl truncl;
+.equ truncl, trunc;
diff --git a/libm/x86/truncf.S b/libm/x86/truncf.S
new file mode 100644
index 0000000..d3e3f4a
--- /dev/null
+++ b/libm/x86/truncf.S
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(truncf)
+ movss 0x4(%esp),%xmm0
+ roundss $0x3,%xmm0,%xmm0
+ movss %xmm0,-0x4(%esp)
+ flds -0x4(%esp)
+ ret
+END(truncf)
diff --git a/libm/x86_64/ceil.S b/libm/x86_64/ceil.S
new file mode 100644
index 0000000..d4492c4
--- /dev/null
+++ b/libm/x86_64/ceil.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(ceil)
+roundsd $0x2,%xmm0,%xmm0
+retq
+END(ceil)
diff --git a/libm/x86_64/ceilf.S b/libm/x86_64/ceilf.S
new file mode 100644
index 0000000..0e1ca95
--- /dev/null
+++ b/libm/x86_64/ceilf.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(ceilf)
+roundss $0x2,%xmm0,%xmm0
+retq
+END(ceilf)
diff --git a/libm/x86_64/floor.S b/libm/x86_64/floor.S
new file mode 100644
index 0000000..dc80e88
--- /dev/null
+++ b/libm/x86_64/floor.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(floor)
+roundsd $0x1,%xmm0,%xmm0
+retq
+END(floor)
diff --git a/libm/x86_64/floorf.S b/libm/x86_64/floorf.S
new file mode 100644
index 0000000..832f9c5
--- /dev/null
+++ b/libm/x86_64/floorf.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(floorf)
+roundss $0x1,%xmm0,%xmm0
+retq
+END(floorf)
diff --git a/libm/x86_64/sqrt.S b/libm/x86_64/sqrt.S
new file mode 100644
index 0000000..ee97026
--- /dev/null
+++ b/libm/x86_64/sqrt.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(sqrt)
+sqrtsd %xmm0,%xmm0
+retq
+END(sqrt)
diff --git a/libm/x86_64/sqrtf.S b/libm/x86_64/sqrtf.S
new file mode 100644
index 0000000..910407f
--- /dev/null
+++ b/libm/x86_64/sqrtf.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(sqrtf)
+sqrtss %xmm0,%xmm0
+retq
+END(sqrtf)
diff --git a/libm/x86_64/trunc.S b/libm/x86_64/trunc.S
new file mode 100644
index 0000000..fe18b40
--- /dev/null
+++ b/libm/x86_64/trunc.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(trunc)
+roundsd $0x3,%xmm0,%xmm0
+retq
+END(trunc)
diff --git a/libm/x86_64/truncf.S b/libm/x86_64/truncf.S
new file mode 100644
index 0000000..eeee1d7
--- /dev/null
+++ b/libm/x86_64/truncf.S
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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.
+
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+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(truncf)
+roundss $0x3,%xmm0,%xmm0
+retq
+END(truncf)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 5d2425f..593785b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -50,11 +50,13 @@
#include "private/UniquePtr.h"
#include "linker.h"
+#include "linker_allocator.h"
#include "linker_debug.h"
#include "linker_environ.h"
+#include "linker_leb128.h"
#include "linker_phdr.h"
#include "linker_relocs.h"
-#include "linker_allocator.h"
+#include "linker_reloc_iterators.h"
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
*
@@ -419,26 +421,41 @@
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
+ TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
+ symbol_name.get_name(), name, 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(), name, reinterpret_cast<void*>(base));
+
return nullptr;
}
// bloom test says "probably yes"...
- uint32_t n = bucket_[hash % nbucket_];
+ uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if (n == 0) {
+ TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
+ symbol_name.get_name(), name, reinterpret_cast<void*>(base));
+
return nullptr;
}
do {
ElfW(Sym)* s = symtab_ + n;
- if (((chain_[n] ^ hash) >> 1) == 0 &&
+ if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
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(), name, reinterpret_cast<void*>(s->st_value),
+ static_cast<size_t>(s->st_size));
return s;
}
- } while ((chain_[n++] & 1) == 0);
+ } while ((gnu_chain_[n++] & 1) == 0);
+
+ TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
+ symbol_name.get_name(), name, reinterpret_cast<void*>(base));
return nullptr;
}
@@ -800,8 +817,8 @@
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 < nbucket_; ++i) {
- uint32_t n = bucket_[i];
+ for (size_t i = 0; i < gnu_nbucket_; ++i) {
+ uint32_t n = gnu_bucket_[i];
if (n == 0) {
continue;
@@ -812,7 +829,7 @@
if (symbol_matches_soaddr(sym, soaddr)) {
return sym;
}
- } while ((chain_[n++] & 1) == 0);
+ } while ((gnu_chain_[n++] & 1) == 0);
}
return nullptr;
@@ -1239,9 +1256,7 @@
}
void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path) {
- if (!get_AT_SECURE()) {
- parse_LD_LIBRARY_PATH(ld_library_path);
- }
+ parse_LD_LIBRARY_PATH(ld_library_path);
}
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) {
@@ -1297,9 +1312,14 @@
}
#endif
-template<typename ElfRelT>
-bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rel) {
+template<typename ElfRelIteratorT>
+bool soinfo::relocate(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);
@@ -1405,16 +1425,16 @@
MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n",
reinterpret_cast<void*>(reloc),
- reinterpret_cast<void*>(base + addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (base + addend);
+ 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*>(base + addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + addend);
+ reinterpret_cast<void*>(load_bias + addend));
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(load_bias + addend);
break;
#if defined(__aarch64__)
@@ -1936,11 +1956,6 @@
break;
case DT_HASH:
- if (nbucket_ != 0) {
- // in case of --hash-style=both, we prefer gnu
- break;
- }
-
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);
@@ -1948,20 +1963,15 @@
break;
case DT_GNU_HASH:
- if (nbucket_ != 0) {
- // in case of --hash-style=both, we prefer gnu
- nchain_ = 0;
- }
-
- nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
+ 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);
- bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
+ gnu_bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
// amend chain for symndx = header[1]
- chain_ = bucket_ + nbucket_ - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[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_, name);
@@ -2051,6 +2061,22 @@
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\"", name);
+ return false;
+
+ case DT_ANDROID_RELSZ:
+ DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", name);
+ 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));
@@ -2069,6 +2095,7 @@
case DT_RELSZ:
DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
return false;
+
#else
case DT_REL:
rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
@@ -2085,6 +2112,22 @@
}
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\"", name);
+ return false;
+
+ case DT_ANDROID_RELASZ:
+ DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", name);
+ return false;
+
// "Indicates that all RELATIVE relocations have been concatenated together,
// and specifies the RELATIVE relocation count."
//
@@ -2092,9 +2135,15 @@
// Not currently used by bionic linker - ignored.
case DT_RELCOUNT:
break;
+
case DT_RELA:
DL_ERR("unsupported DT_RELA in \"%s\"", name);
return false;
+
+ case DT_RELASZ:
+ DL_ERR("unsupported DT_RELASZ in \"%s\"", name);
+ return false;
+
#endif
case DT_INIT:
init_func_ = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
@@ -2234,7 +2283,7 @@
DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
return false;
}
- if (nbucket_ == 0) {
+ if (nbucket_ == 0 && gnu_nbucket_ == 0) {
DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name);
return false;
}
@@ -2249,7 +2298,8 @@
return true;
}
-bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) {
+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) {
@@ -2270,29 +2320,63 @@
}
#endif
+ if (android_relocs_ != nullptr) {
+ // check signature
+ if (android_relocs_size_ > 3 &&
+ android_relocs_[0] == 'A' &&
+ android_relocs_[1] == 'P' &&
+ (android_relocs_[2] == 'U' || android_relocs_[2] == 'S') &&
+ android_relocs_[3] == '2') {
+ DEBUG("[ android relocating %s ]", name);
+
+ bool relocated = false;
+ const uint8_t* packed_relocs = android_relocs_ + 4;
+ const size_t packed_relocs_size = android_relocs_size_ - 4;
+
+ if (android_relocs_[2] == 'U') {
+ relocated = relocate(
+ packed_reloc_iterator<leb128_decoder>(
+ leb128_decoder(packed_relocs, packed_relocs_size)),
+ global_group, local_group);
+ } else { // android_relocs_[2] == 'S'
+ relocated = relocate(
+ 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 ]", name);
- if (!relocate(rela_, rela_count_, global_group, local_group)) {
+ if (!relocate(plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
return false;
}
}
if (plt_rela_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
+ if (!relocate(plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
return false;
}
}
#else
if (rel_ != nullptr) {
DEBUG("[ relocating %s ]", name);
- if (!relocate(rel_, rel_count_, global_group, local_group)) {
+ if (!relocate(plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) {
return false;
}
}
if (plt_rel_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
+ if (!relocate(plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) {
return false;
}
}
diff --git a/linker/linker.h b/linker/linker.h
index 2afbaf6..e4681eb 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -286,8 +286,8 @@
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
void call_function(const char* function_name, linker_function_t function);
- template<typename ElfRelT>
- bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+ template<typename ElfRelIteratorT>
+ bool relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
private:
// This part of the structure is only available
@@ -309,12 +309,19 @@
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_;
+
friend soinfo* get_libdl_info();
};
diff --git a/linker/linker_leb128.h b/linker/linker_leb128.h
new file mode 100644
index 0000000..d5c6488
--- /dev/null
+++ b/linker/linker_leb128.h
@@ -0,0 +1,87 @@
+/*
+ * 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_LEB128_H
+#define _LINKER_LEB128_H
+
+#include <stdint.h>
+
+// Helper classes for decoding LEB128, used in packed relocation data.
+// http://en.wikipedia.org/wiki/LEB128
+
+class leb128_decoder {
+ public:
+ leb128_decoder(const uint8_t* buffer, size_t count)
+ : current_(buffer), end_(buffer + count) { }
+
+ size_t pop_front() {
+ size_t value = 0;
+
+ size_t shift = 0;
+ uint8_t byte;
+
+ do {
+ if (current_ >= end_) {
+ __libc_fatal("leb128_decoder ran out of bounds");
+ }
+ byte = *current_++;
+ value |= static_cast<size_t>(byte & 127) << shift;
+ shift += 7;
+ } while (byte & 128);
+
+ return value;
+ }
+
+ private:
+ const uint8_t* current_;
+ const uint8_t* const end_;
+};
+
+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("leb128_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_LEB128_H
+
diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp
index 7fbde3d..f0bde55 100644
--- a/linker/linker_mips.cpp
+++ b/linker/linker_mips.cpp
@@ -29,10 +29,32 @@
#include "linker.h"
#include "linker_debug.h"
#include "linker_relocs.h"
+#include "linker_reloc_iterators.h"
+#include "linker_leb128.h"
-template<>
-bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rel) {
+template bool soinfo::relocate<plain_reloc_iterator>(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>>(
+ packed_reloc_iterator<sleb128_decoder>&& rel_iterator,
+ const soinfo_list_t& global_group,
+ const soinfo_list_t& local_group);
+
+template bool soinfo::relocate<packed_reloc_iterator<leb128_decoder>>(
+ packed_reloc_iterator<leb128_decoder>&& rel_iterator,
+ const soinfo_list_t& global_group,
+ const soinfo_list_t& local_group);
+
+template <typename ElfRelIteratorT>
+bool soinfo::relocate(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);
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 91a2fb8..38e6262 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -332,7 +332,7 @@
return false;
}
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
- start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
+ start = mmap(nullptr, load_size_, PROT_NONE, mmap_flags, -1, 0);
if (start == MAP_FAILED) {
DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
return false;
diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h
new file mode 100644
index 0000000..5db31f9
--- /dev/null
+++ b/linker/linker_reloc_iterators.h
@@ -0,0 +1,152 @@
+/*
+ * 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>
+
+#define RELOCATION_GROUPED_BY_INFO_FLAG 1
+#define RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG 2
+#define RELOCATION_GROUPED_BY_ADDEND_FLAG 4
+#define RELOCATION_GROUP_HAS_ADDEND_FLAG 8
+
+#define RELOCATION_GROUPED_BY_INFO(flags) (((flags) & RELOCATION_GROUPED_BY_INFO_FLAG) != 0)
+#define RELOCATION_GROUPED_BY_OFFSET_DELTA(flags) (((flags) & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0)
+#define RELOCATION_GROUPED_BY_ADDEND(flags) (((flags) & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0)
+#define RELOCATION_GROUP_HAS_ADDEND(flags) (((flags) & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0)
+
+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 (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) {
+ reloc_.r_offset += group_r_offset_delta_;
+ } else {
+ reloc_.r_offset += decoder_.pop_front();
+ }
+
+ if (!RELOCATION_GROUPED_BY_INFO(group_flags_)) {
+ reloc_.r_info = decoder_.pop_front();
+ }
+
+#if defined(USE_RELA)
+ if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && !RELOCATION_GROUPED_BY_ADDEND(group_flags_)) {
+ 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 (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) {
+ group_r_offset_delta_ = decoder_.pop_front();
+ }
+
+ if (RELOCATION_GROUPED_BY_INFO(group_flags_)) {
+ reloc_.r_info = decoder_.pop_front();
+ }
+
+ if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && RELOCATION_GROUPED_BY_ADDEND(group_flags_)) {
+#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 (!RELOCATION_GROUP_HAS_ADDEND(group_flags_)) {
+ reloc_.r_addend = 0;
+#endif
+ }
+
+ relocation_group_index_ = 0;
+ return true;
+ }
+
+ 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/tools/Android.mk b/tools/Android.mk
new file mode 100644
index 0000000..4dd66fe
--- /dev/null
+++ b/tools/Android.mk
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-subdir-makefiles)
diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk
new file mode 100644
index 0000000..99a39c0
--- /dev/null
+++ b/tools/relocation_packer/Android.mk
@@ -0,0 +1,96 @@
+#
+# 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.
+#
+
+common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := \
+ src/debug.cc \
+ src/delta_encoder.cc \
+ src/elf_file.cc \
+ src/leb128.cc \
+ src/packer.cc \
+ src/sleb128.cc \
+
+LOCAL_STATIC_LIBRARIES := libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf
+LOCAL_MODULE := lib_relocation_packer
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := src/main.cc
+LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
+
+LOCAL_MODULE := relocation_packer
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := \
+ src/debug_unittest.cc \
+ src/delta_encoder_unittest.cc \
+ src/elf_file_unittest.cc \
+ src/leb128_unittest.cc \
+ src/sleb128_unittest.cc \
+ src/packer_unittest.cc \
+
+LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_MODULE := relocation_packer_unit_tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+# $(1) library name
+define copy-test-library
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := $(1)
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := test_data/$(1)
+include $(BUILD_PREBUILT)
+endef
+
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))
diff --git a/tools/relocation_packer/LICENSE b/tools/relocation_packer/LICENSE
new file mode 100644
index 0000000..972bb2e
--- /dev/null
+++ b/tools/relocation_packer/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// 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/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT
new file mode 100644
index 0000000..071ab5d
--- /dev/null
+++ b/tools/relocation_packer/README.TXT
@@ -0,0 +1,135 @@
+Introduction:
+-------------
+
+Relative relocations are the bulk of dynamic relocations (the .rel.dyn
+or .rela.dyn sections) in libchrome.<version>.so. The ELF standard
+representation of them is wasteful.
+
+Packing uses a combination of run length encoding, delta encoding, and LEB128
+encoding to store them more efficiently. Packed relocations are placed in
+a new .android.rel.dyn or .android.rela.dyn section. Packing reduces
+the footprint of libchrome.<version>.so in the filesystem, in APK downloads,
+and in memory when loaded on the device.
+
+A packed libchrome.<version>.so is designed so that it can be loaded directly
+on Android, but requires the explicit support of a crazy linker that has been
+extended to understand packed relocations. Packed relocations are currently
+only supported on ARM.
+
+A packed libchrome.<version>.so cannot currently be used with the standard
+Android runtime linker.
+
+See src/*.h for design and implementation notes.
+
+
+Notes:
+------
+
+Packing does not adjust debug data. An unstripped libchrome.<version>.so
+can be packed and will run, but may no longer be useful for debugging.
+
+Unpacking on the device requires the explicit support of an extended crazy
+linker. Adds the following new .dynamic tags, used by the crazy linker to
+find the packed .android.rel.dyn or .android.rela.dyn section data:
+
+ DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d)
+ - The offset of packed relocation data in libchrome.<version>.so
+ DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e)
+ - The size of packed relocation data in bytes
+
+32 bit ARM libraries use relocations without addends. 64 bit ARM libraries
+use relocations with addends. The packing strategy necessarily differs for
+the two relocation types.
+
+Where libchrome.<version>.so contains relocations without addends, the format
+of .android.rel.dyn data is:
+
+ "APR1" identifier
+ N: the number of count-delta pairs in the encoding
+ A: the initial offset
+ N * C,D: N count-delta pairs
+
+Where libchrome.<version>.so contains relocations with addends, the format
+of .android.rela.dyn data is:
+
+ "APA1" identifier
+ N: the number of addr-addend delta pairs in the encoding
+ N * A,V: N addr-addend delta pairs
+
+All numbers in the encoding stream are stored as LEB128 values. For details
+see http://en.wikipedia.org/wiki/LEB128.
+
+The streaming unpacking algorithm for 32 bit ARM is:
+
+ skip over "APR1"
+ pairs, addr = next leb128 value, next leb128 value
+ emit R_ARM_RELATIVE relocation with r_offset = addr
+ while pairs:
+ count, delta = next leb128 value, next leb128 value
+ while count:
+ addr += delta
+ emit R_ARM_RELATIVE relocation with r_offset = addr
+ count--
+ pairs--
+
+The streaming unpacking algorithm for 64 bit ARM is:
+
+ skip over "APA1"
+ pairs = next signed leb128 value
+ addr, addend = 0, 0
+ while pairs:
+ addr += next signed leb128 value
+ addend += next signed leb128 value
+ emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend
+ pairs--
+
+
+Usage instructions:
+-------------------
+
+To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and
+then run the tool:
+
+ echo -n 'NULL' >/tmp/small
+ if file libchrome.<version>.so | grep -q 'ELF 32'; then
+ arm-linux-androideabi-objcopy
+ --add-section .android.rel.dyn=/tmp/small
+ libchrome.<version>.so libchrome.<version>.so.packed
+ else
+ aarch64-linux-android-objcopy
+ --add-section .android.rela.dyn=/tmp/small
+ libchrome.<version>.so libchrome.<version>.so.packed
+ fi
+ rm /tmp/small
+ relocation_packer libchrome.<version>.so.packed
+
+To unpack and restore the shared library to its original state:
+
+ cp libchrome.<version>.so.packed unpackable
+ relocation_packer -u unpackable
+ if file libchrome.<version>.so | grep -q 'ELF 32'; then
+ arm-linux-androideabi-objcopy \
+ --remove-section=.android.rel.dyn unpackable libchrome.<version>.so
+ else
+ aarch64-linux-android-objcopy \
+ --remove-section=.android.rela.dyn unpackable libchrome.<version>.so
+ endif
+ rm unpackable
+
+
+Bugs & TODOs:
+-------------
+
+Requires two free slots in the .dynamic section. Uses these to add data that
+tells the crazy linker where to find the packed relocation data. Fails
+if insufficient free slots exist (use gold --spare-dynamic-slots to increase
+the allocation).
+
+Requires libelf 0.158 or later. Earlier libelf releases may be buggy in
+ways that prevent the packer from working correctly.
+
+
+Testing:
+--------
+
+Unittests run under gtest, on the host system.
diff --git a/tools/relocation_packer/src/debug.cc b/tools/relocation_packer/src/debug.cc
new file mode 100644
index 0000000..29d7ab0
--- /dev/null
+++ b/tools/relocation_packer/src/debug.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+
+namespace relocation_packer {
+
+// Construct a new message logger. Prints if level is less than or equal to
+// the level set with SetVerbose() and predicate is true.
+Logger::Logger(Severity severity, int level, bool predicate) {
+ severity_ = severity;
+ level_ = level;
+ predicate_ = predicate;
+}
+
+// On destruction, flush and print the strings accumulated. Abort if FATAL.
+Logger::~Logger() {
+ if (predicate_) {
+ if (level_ <= max_level_) {
+ std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_;
+ std::string tag;
+ switch (severity_) {
+ case INFO: tag = "INFO"; break;
+ case WARNING: tag = "WARNING"; break;
+ case ERROR: tag = "ERROR"; break;
+ case FATAL: tag = "FATAL"; break;
+ }
+ stream_.flush();
+ *log << tag << ": " << stream_.str() << std::endl;
+ }
+ if (severity_ == FATAL)
+ abort();
+ }
+}
+
+// Reset to initial state.
+void Logger::Reset() {
+ max_level_ = -1;
+ info_stream_ = &std::cout;
+ error_stream_ = &std::cerr;
+}
+
+// Verbosity. Not thread-safe.
+int Logger::max_level_ = -1;
+
+// Logging streams. Not thread-safe.
+std::ostream* Logger::info_stream_ = &std::cout;
+std::ostream* Logger::error_stream_ = &std::cerr;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/debug.h b/tools/relocation_packer/src/debug.h
new file mode 100644
index 0000000..48be6c1
--- /dev/null
+++ b/tools/relocation_packer/src/debug.h
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Logging and checks. Avoids a dependency on base.
+//
+// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL.
+// INFO prints to stdout, the others to stderr. FATAL aborts after printing.
+//
+// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent.
+//
+// VLOG(level) logs INFO messages where level is less than or equal to the
+// verbosity level set with SetVerbose().
+//
+// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true,
+// else silent.
+//
+// CHECK(predicate) logs a FATAL error if predicate is false.
+// NOTREACHED() always aborts.
+// Log streams can be changed with SetStreams(). Logging is not thread-safe.
+//
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+
+#include <limits.h>
+#include <ostream>
+#include <sstream>
+
+namespace relocation_packer {
+
+class Logger {
+ public:
+ enum Severity {INFO = 0, WARNING, ERROR, FATAL};
+
+ // Construct a new message logger. Prints if level is less than or
+ // equal to the level set with SetVerbose() and predicate is true.
+ // |severity| is an enumerated severity.
+ // |level| is the verbosity level.
+ // |predicate| controls if the logger prints or is silent.
+ Logger(Severity severity, int level, bool predicate);
+
+ // On destruction, flush and print the strings accumulated in stream_.
+ ~Logger();
+
+ // Return the stream for this logger.
+ std::ostream& GetStream() { return stream_; }
+
+ // Set verbosity level. Messages with a level less than or equal to
+ // this level are printed, others are discarded. Static, not thread-safe.
+ static void SetVerbose(int level) { max_level_ = level; }
+
+ // Set info and error logging streams. Static, not thread-safe.
+ static void SetStreams(std::ostream* info_stream,
+ std::ostream* error_stream) {
+ info_stream_ = info_stream;
+ error_stream_ = error_stream;
+ }
+
+ // Reset to initial state.
+ static void Reset();
+
+ private:
+ // Message severity, verbosity level, and predicate.
+ Severity severity_;
+ int level_;
+ bool predicate_;
+
+ // String stream, accumulates message text.
+ std::ostringstream stream_;
+
+ // Verbosity for INFO messages. Not thread-safe.
+ static int max_level_;
+
+ // Logging streams. Not thread-safe.
+ static std::ostream* info_stream_;
+ static std::ostream* error_stream_;
+};
+
+} // namespace relocation_packer
+
+// Make logging severities visible globally.
+typedef relocation_packer::Logger::Severity LogSeverity;
+using LogSeverity::INFO;
+using LogSeverity::WARNING;
+using LogSeverity::ERROR;
+using LogSeverity::FATAL;
+
+// LOG(severity) prints a message with the given severity, and aborts if
+// severity is FATAL. LOG_IF(severity, predicate) does the same but only if
+// predicate is true. INT_MIN is guaranteed to be less than or equal to
+// any verbosity level.
+#define LOG(severity) \
+ (relocation_packer::Logger(severity, INT_MIN, true).GetStream())
+#define LOG_IF(severity, predicate) \
+ (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream())
+
+// VLOG(level) prints its message as INFO if level is less than or equal to
+// the current verbosity level.
+#define VLOG(level) \
+ (relocation_packer::Logger(INFO, (level), true).GetStream())
+#define VLOG_IF(level, predicate) \
+ (relocation_packer::Logger(INFO, (level), (predicate)).GetStream())
+
+// CHECK(predicate) fails with a FATAL log message if predicate is false.
+#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << __FUNCTION__ << ": CHECK '" #predicate "' failed")
+
+// NOTREACHED() always fails with a FATAL log message.
+#define NOTREACHED(_) (LOG(FATAL) \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << __FUNCTION__ << ": NOTREACHED() hit")
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
diff --git a/tools/relocation_packer/src/debug_unittest.cc b/tools/relocation_packer/src/debug_unittest.cc
new file mode 100644
index 0000000..b31e2ae
--- /dev/null
+++ b/tools/relocation_packer/src/debug_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <sstream>
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Debug, Log) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ LOG(INFO) << "INFO log message";
+ LOG(WARNING) << "WARNING log message";
+ LOG(ERROR) << "ERROR log message";
+
+ EXPECT_EQ("INFO: INFO log message\n", info.str());
+ EXPECT_EQ("WARNING: WARNING log message\n"
+ "ERROR: ERROR log message\n", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, LogIf) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ LOG_IF(INFO, true) << "INFO log message";
+ LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT";
+ LOG_IF(WARNING, true) << "WARNING log message";
+ LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT";
+ LOG_IF(ERROR, true) << "ERROR log message";
+ LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT";
+ LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: INFO log message\n", info.str());
+ EXPECT_EQ("WARNING: WARNING log message\n"
+ "ERROR: ERROR log message\n", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, Vlog) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("", info.str());
+ EXPECT_EQ("", error.str());
+
+ Logger::SetVerbose(1);
+
+ VLOG(0) << "VLOG 0 INFO log message";
+ VLOG(1) << "VLOG 1 INFO log message";
+ VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+ "INFO: VLOG 1 INFO log message\n", info.str());
+ EXPECT_EQ("", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, VlogIf) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("", info.str());
+ EXPECT_EQ("", error.str());
+
+ Logger::SetVerbose(1);
+
+ VLOG_IF(0, true) << "VLOG 0 INFO log message";
+ VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(1, true) << "VLOG 1 INFO log message";
+ VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+ "INFO: VLOG 1 INFO log message\n", info.str());
+ EXPECT_EQ("", error.str());
+ Logger::Reset();
+}
+
+TEST(DebugDeathTest, Fatal) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message");
+ EXPECT_DEATH(
+ LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message");
+}
+
+TEST(DebugDeathTest, Check) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ CHECK(0 == 0);
+ EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed");
+}
+
+TEST(DebugDeathTest, NotReached) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit");
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc
new file mode 100644
index 0000000..8349d7c
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder.cc
@@ -0,0 +1,307 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+
+#include "debug.h"
+
+static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
+static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
+static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
+static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
+
+static bool is_relocation_grouped_by_info(uint64_t flags) {
+ return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
+}
+
+static bool is_relocation_grouped_by_offset_delta(uint64_t flags) {
+ return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
+}
+
+static bool is_relocation_grouped_by_addend(uint64_t flags) {
+ return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
+}
+
+static bool is_relocation_group_has_addend(uint64_t flags) {
+ return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
+}
+
+namespace relocation_packer {
+
+// Encode relocations into a delta encoded (packed) representation.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
+ std::vector<ElfAddr>* packed) {
+ if (relocations.size() == 0)
+ return;
+
+ // Start with the relocation count, then append groups
+ // TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section
+ packed->push_back(static_cast<ElfAddr>(relocations.size()));
+
+ // lets write starting offset (offset of the first reloc - first delta)
+ ElfAddr start_offset = relocations.size() > 1 ?
+ relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) :
+ relocations[0].r_offset;
+
+ packed->push_back(start_offset);
+
+ // this one is used to calculate delta
+ ElfAddr previous_addend = 0;
+ ElfAddr previous_offset = start_offset;
+
+ for (size_t group_start = 0; group_start < relocations.size(); ) {
+ ElfAddr group_flags = 0;
+ ElfAddr group_offset_delta = 0;
+ ElfAddr group_addend = 0;
+ ElfAddr group_info = 0;
+
+ ElfAddr group_size = 0;
+
+ DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags,
+ &group_offset_delta, &group_info, &group_addend);
+
+ // write the group header
+ packed->push_back(group_size);
+ packed->push_back(group_flags);
+
+ if (is_relocation_grouped_by_offset_delta(group_flags)) {
+ packed->push_back(group_offset_delta);
+ }
+
+ if (is_relocation_grouped_by_info(group_flags)) {
+ packed->push_back(group_info);
+ }
+
+ if (is_relocation_group_has_addend(group_flags) &&
+ is_relocation_grouped_by_addend(group_flags)) {
+ packed->push_back(group_addend - previous_addend);
+ previous_addend = group_addend;
+ }
+
+ for (size_t i = 0; i < static_cast<size_t>(group_size); ++i) {
+ CHECK((group_start + i) < relocations.size());
+ const ElfRela* relocation = &relocations[group_start + i];
+
+ if (!is_relocation_grouped_by_offset_delta(group_flags)) {
+ packed->push_back(relocation->r_offset - previous_offset);
+ }
+ previous_offset = relocation->r_offset;
+
+ if (!is_relocation_grouped_by_info(group_flags)) {
+ packed->push_back(relocation->r_info);
+ }
+
+ if (is_relocation_group_has_addend(group_flags) &&
+ !is_relocation_grouped_by_addend(group_flags)) {
+ packed->push_back(relocation->r_addend - previous_addend);
+ previous_addend = relocation->r_addend;
+ }
+ }
+
+ // If the relocation group does not have an addend - reset it to 0
+ // to simplify addend computation for the group following this one.
+ if (!is_relocation_group_has_addend(group_flags)) {
+ previous_addend = 0;
+ }
+
+ group_start += group_size;
+ }
+}
+
+// Decode relocations from a delta encoded (packed) representation.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
+ std::vector<ElfRela>* relocations) {
+ if (packed.size() < 5) {
+ return;
+ }
+
+ size_t ndx = 0;
+ ElfAddr current_count = 0;
+ ElfAddr total_count = packed[ndx++];
+
+ ElfAddr offset = packed[ndx++];
+ ElfAddr info = 0;
+ ElfAddr addend = 0;
+
+ while(current_count < total_count) {
+ // read group
+ ElfAddr group_size = packed[ndx++];
+ ElfAddr group_flags = packed[ndx++];
+ ElfAddr group_offset_delta = 0;
+
+ if (is_relocation_grouped_by_offset_delta(group_flags)) {
+ group_offset_delta = packed[ndx++];
+ }
+
+ if (is_relocation_grouped_by_info(group_flags)) {
+ info = packed[ndx++];
+ }
+
+ if (is_relocation_group_has_addend(group_flags) &&
+ is_relocation_grouped_by_addend(group_flags)) {
+ addend += packed[ndx++];
+ }
+
+ // now read not grouped info
+ for (ElfAddr i = 0; i<group_size; ++i) {
+ if (is_relocation_grouped_by_offset_delta(group_flags)) {
+ offset += group_offset_delta;
+ } else {
+ offset += packed[ndx++];
+ }
+
+ if (!is_relocation_grouped_by_info(group_flags)) {
+ info = packed[ndx++];
+ }
+
+ if (is_relocation_group_has_addend(group_flags) &&
+ !is_relocation_grouped_by_addend(group_flags)) {
+ addend += packed[ndx++];
+ }
+
+ ElfRela reloc;
+ reloc.r_offset = offset;
+ reloc.r_info = info;
+ reloc.r_addend = is_relocation_group_has_addend(group_flags) ? addend : 0;
+ relocations->push_back(reloc);
+ }
+
+ if (!is_relocation_group_has_addend(group_flags)) {
+ addend = 0;
+ }
+
+ current_count += group_size;
+ }
+}
+
+// This function detects a way to group reloc_one and reloc_two, sets up group_flags
+// and initializes values for corresponding group_ fields. For example if relocations
+// can be grouped by r_info the function will set group_info variable.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::DetectGroupFields(const ElfRela& reloc_one,
+ const ElfRela& reloc_two,
+ ElfAddr current_offset_delta,
+ ElfAddr* group_flags,
+ ElfAddr* group_offset_delta,
+ ElfAddr* group_info,
+ ElfAddr* group_addend) {
+ *group_flags = 0;
+
+ const ElfAddr offset_delta = static_cast<ElfAddr>(reloc_two.r_offset) -
+ static_cast<ElfAddr>(reloc_one.r_offset);
+
+ if (offset_delta == current_offset_delta) {
+ *group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
+ if (group_offset_delta != nullptr) {
+ *group_offset_delta = current_offset_delta;
+ }
+ }
+
+ if (reloc_one.r_info == reloc_two.r_info) {
+ *group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG;
+ if (group_info != nullptr) {
+ *group_info = reloc_one.r_info;
+ }
+ }
+
+ if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) {
+ *group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG;
+ if (reloc_one.r_addend == reloc_two.r_addend) {
+ *group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG;
+ if (group_addend != nullptr) {
+ *group_addend = reloc_one.r_addend;
+ }
+ }
+ }
+}
+
+// This function is used to detect if there is better group available
+// during RelocationDeltaCodec<ELF>::DetectGroup processing.
+// Current implementation prefers having groups without addend (== zero addend)
+// to any other groups field with the ratio 3:1. This is because addend tends
+// to be more unevenly distributed than other fields.
+static uint32_t group_weight(uint64_t flags) {
+ uint32_t weight = 0;
+ if (!is_relocation_group_has_addend(flags)) {
+ weight += 3;
+ } else if (is_relocation_grouped_by_addend(flags)) {
+ weight += 1;
+ }
+
+ if (is_relocation_grouped_by_offset_delta(flags)) {
+ weight += 1;
+ }
+
+ if (is_relocation_grouped_by_info(flags)) {
+ weight += 1;
+ }
+
+ return weight;
+}
+
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::DetectGroup(const std::vector<ElfRela>& relocations,
+ size_t group_starts_with, ElfAddr previous_offset,
+ ElfAddr* group_size, ElfAddr* group_flags,
+ ElfAddr* group_offset_delta, ElfAddr* group_info,
+ ElfAddr* group_addend) {
+ CHECK(group_starts_with < relocations.size());
+ CHECK(group_flags != nullptr);
+
+ const ElfRela& reloc_one = relocations[group_starts_with++];
+ if (group_starts_with == relocations.size()) {
+ *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
+ *group_size = 1;
+ return;
+ }
+
+ const ElfAddr offset_delta = reloc_one.r_offset - previous_offset;
+
+ // detect group_flags
+ DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags,
+ group_offset_delta, group_info, group_addend);
+
+ if (group_starts_with + 1 == relocations.size()) {
+ *group_size = 2;
+ return;
+ }
+
+ ElfAddr cnt = 1;
+ for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) {
+ ElfAddr candidate_flags;
+ // check if next group (reloc_current; reloc_next) has better grouped_by flags
+ DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags,
+ nullptr, nullptr, nullptr);
+
+ if (group_weight(*group_flags) < group_weight(candidate_flags)) {
+ break;
+ }
+ cnt++;
+
+ if (candidate_flags != *group_flags) {
+ break;
+ }
+
+ if (i + 1 == relocations.size() - 1) { // last one
+ cnt++;
+ }
+ }
+
+ // if as a result of checking candidates we ended up with cnt == 1
+ // reset flags to the default state
+ if (cnt == 1) {
+ *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
+ }
+
+ *group_size = cnt;
+}
+
+template class RelocationDeltaCodec<ELF32_traits>;
+template class RelocationDeltaCodec<ELF64_traits>;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h
new file mode 100644
index 0000000..46c324c
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Delta encode and decode REL/RELA section of elf file.
+//
+// The encoded data format is sequence of elements of ElfAddr type (unsigned long):
+//
+// [00] relocation_count - the total count of relocations
+// [01] initial r_offset - this is initial r_offset for the
+// relocation table.
+// followed by group structures:
+// [02] group
+// ...
+// [nn] group
+
+// the generalized format of the group is (! - always present ? - depends on group_flags):
+// --------------
+// ! group_size
+// ! group_flags
+// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set
+// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set
+// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND
+// flag is set
+//
+// The group description is followed by individual relocations.
+// please note that there is a case when individual relocation
+// section could be empty - that is if every field ends up grouped.
+//
+// The format for individual relocations section is:
+// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
+// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
+// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
+//
+// For example lets pack the following relocations:
+//
+// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
+// Offset Info Type Symbol's Value Symbol's Name + Addend
+// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8
+// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc
+// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0
+// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4
+// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804
+// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818
+// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c
+// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840
+// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854
+// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868
+// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c
+// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890
+// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4
+// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8
+//
+// The header is going to be
+// [00] 14 <- count
+// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta,
+// the delta is 8 in this case)
+// -- starting the first and only group
+// [03] 14 <- group size
+// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA
+// | RELOCATION_GROUPED_BY_INFO
+// [04] 8 <- offset delta
+// [05] 0x403 <- r_info
+// -- end of group definition, starting list of r_addend deltas
+// [06] 0x177a8
+// [07] 0x24 = 177cc - 177a8
+// [08] 0x14 = 177e0 - 177cc
+// [09] 0x14 = 177f4 - 177e0
+// [10] 0x10 = 17804 - 177f4
+// [11] 0x14 = 17818 - 17804
+// [12] 0x14 = 1782c - 17818
+// [13] 0x14 = 17840 - 1782c
+// [14] 0x14 = 17854 - 17840
+// [15] 0x14 = 17868 - 17854
+// [16] 0x14 = 1787c - 17868
+// [17] 0x14 = 17890 - 1787c
+// [18] 0x14 = 178a4 - 17890
+// [19] 0x14 = 178b8 - 178a4
+// -- the end.
+
+// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can
+// save us more bytes...
+
+// The input ends when sum(group_size) == relocation_count
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationDeltaCodec packs vectors of relative relocations with
+// addends into more compact forms, and unpacks them to reproduce the
+// pre-packed data.
+template <typename ELF>
+class RelocationDeltaCodec {
+ public:
+ typedef typename ELF::Addr ElfAddr;
+ typedef typename ELF::Rela ElfRela;
+
+ // Encode relocations with addends into a more compact form.
+ // |relocations| is a vector of relative relocation with addend structs.
+ // |packed| is the vector of packed words into which relocations are packed.
+ static void Encode(const std::vector<ElfRela>& relocations,
+ std::vector<ElfAddr>* packed);
+
+ // Decode relative relocations with addends from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relative relocations.
+ static void Decode(const std::vector<ElfAddr>& packed,
+ std::vector<ElfRela>* relocations);
+
+ private:
+ static void DetectGroup(const std::vector<ElfRela>& relocations,
+ size_t group_starts_with, ElfAddr previous_offset,
+ ElfAddr* group_size, ElfAddr* group_flags,
+ ElfAddr* group_offset_delta, ElfAddr* group_info,
+ ElfAddr* group_addend);
+
+ static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two,
+ ElfAddr current_offset_delta, ElfAddr* group_flags,
+ ElfAddr* group_offset_delta, ElfAddr* group_info,
+ ElfAddr* group_addend);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
diff --git a/tools/relocation_packer/src/delta_encoder_unittest.cc b/tools/relocation_packer/src/delta_encoder_unittest.cc
new file mode 100644
index 0000000..06d9c96
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+#include "elf.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+template <typename T>
+void AddRelocation(uint32_t addr,
+ uint32_t info,
+ int32_t addend,
+ std::vector<T>* relocations) {
+ T relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = info;
+ relocation.r_addend = addend;
+ relocations->push_back(relocation);
+}
+
+template <typename T>
+bool CheckRelocation(uint32_t addr,
+ uint32_t info,
+ int32_t addend,
+ const T& relocation) {
+ return relocation.r_offset == addr &&
+ relocation.r_info == info &&
+ relocation.r_addend == addend;
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+template <typename ELF>
+static void encode() {
+ std::vector<typename ELF::Rela> relocations;
+ std::vector<typename ELF::Addr> packed;
+
+ RelocationDeltaCodec<ELF> codec;
+
+ codec.Encode(relocations, &packed);
+
+ ASSERT_EQ(0U, packed.size());
+
+ // Initial relocation.
+ AddRelocation(0xf00d0000, 11U, 10000, &relocations);
+
+ codec.Encode(relocations, &packed);
+
+ // size of reloc table, size of group, flags, 3 fields, zero
+ EXPECT_EQ(7U, packed.size());
+ // One pair present.
+ size_t ndx = 0;
+ EXPECT_EQ(1U, packed[ndx++]);
+ EXPECT_EQ(0xf00d0000, packed[ndx++]);
+ EXPECT_EQ(1U, packed[ndx++]); // group_size
+ EXPECT_EQ(8U, packed[ndx++]); // flags
+ // Delta from the neutral element is zero
+ EXPECT_EQ(0U, packed[ndx++]); // offset_delta
+ EXPECT_EQ(11U, packed[ndx++]); // info
+ EXPECT_EQ(10000U, packed[ndx++]); // addend_delta
+
+ // Add a second relocation, 4 byte offset delta, 12 byte addend delta.
+ // same info
+ AddRelocation(0xf00d0004, 11U, 10012, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ ndx = 0;
+ EXPECT_EQ(8U, packed.size());
+
+ EXPECT_EQ(2U, packed[ndx++]); // relocs count
+ EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
+ EXPECT_EQ(2U, packed[ndx++]); // group count
+ EXPECT_EQ(11U, packed[ndx++]); // flags
+ EXPECT_EQ(4U, packed[ndx++]); // group offset delta
+ EXPECT_EQ(11U, packed[ndx++]); // info
+
+ EXPECT_EQ(10000U, packed[ndx++]); // addend delta
+ EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+ // Add a third relocation, 4 byte offset delta, 12 byte addend delta.
+ // different info
+ AddRelocation(0xf00d0008, 41U, 10024, &relocations);
+
+ // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+ AddRelocation(0xf00d0010, 42U, 10000, &relocations);
+ AddRelocation(0xf00d0018, 42U, 9976, &relocations);
+ AddRelocation(0xf00d0020, 42U, 9952, &relocations);
+
+ AddRelocation(0xf00d2028, 1042U, 0, &relocations);
+ AddRelocation(0xf00d2030, 3442U, 0, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ ndx = 0;
+ EXPECT_EQ(26U, packed.size());
+ // Total number of relocs
+ EXPECT_EQ(8U, packed[ndx++]);
+ EXPECT_EQ(0xf00cfffc, packed[ndx++]);
+ // 2 in first group
+ EXPECT_EQ(2U, packed[ndx++]);
+ EXPECT_EQ(11U, packed[ndx++]); //flags
+ EXPECT_EQ(4U, packed[ndx++]); // group offset delta
+ EXPECT_EQ(11U, packed[ndx++]); // info
+
+ // Initial relocation.
+ EXPECT_EQ(10000U, packed[ndx++]); // addend delta
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+ // second group has only one reloc
+ EXPECT_EQ(1U, packed[ndx++]); // count
+ EXPECT_EQ(8U, packed[ndx++]); // flags
+
+ EXPECT_EQ(4U, packed[ndx++]); // offset delta
+ EXPECT_EQ(41U, packed[ndx++]); // info
+ EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+ // next - 3 relocs grouped by info
+ EXPECT_EQ(3U, packed[ndx++]); // count
+ EXPECT_EQ(11U, packed[ndx++]); // flags
+ EXPECT_EQ(8U, packed[ndx++]); // group offset delta
+ EXPECT_EQ(42U, packed[ndx++]); // info
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+ EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+ EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+
+ // and last - 2 relocations without addend
+ EXPECT_EQ(2U, packed[ndx++]);
+ EXPECT_EQ(0U, packed[ndx++]); // flags
+ // offset_deltas and r_infos for next 2 relocations
+ EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta
+ EXPECT_EQ(1042U, packed[ndx++]); // r_info
+ EXPECT_EQ(0x8U, packed[ndx++]); // offset delta
+ EXPECT_EQ(3442U, packed[ndx++]); // r_info
+
+ EXPECT_EQ(packed.size(), ndx);
+}
+
+TEST(Delta, Encode32) {
+ encode<ELF32_traits>();
+}
+
+TEST(Delta, Encode64) {
+ encode<ELF64_traits>();
+}
+
+template <typename ELF>
+static void decode() {
+ std::vector<typename ELF::Addr> packed;
+ std::vector<typename ELF::Rela> relocations;
+
+ RelocationDeltaCodec<ELF> codec;
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(0U, relocations.size());
+
+ // Six pairs.
+ packed.push_back(6U); // count
+ packed.push_back(0xc0ddfffc); // base offset
+ packed.push_back(3U); // group count
+ packed.push_back(11U); // flags
+ packed.push_back(4U); // offset delta
+ packed.push_back(11U); // info
+ // Initial relocation.
+ packed.push_back(10000U);
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ packed.push_back(12U); // addend
+ packed.push_back(12U); // addend
+
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ packed.push_back(1U); // group count
+ packed.push_back(9U); // flags
+ packed.push_back(11U); // info
+
+ packed.push_back(8U);
+ packed.push_back(static_cast<typename ELF::Addr>(-24));
+ // next group with 2 relocs
+ packed.push_back(2U); // group count
+ packed.push_back(11U); // flags
+ packed.push_back(8U); // offset
+ packed.push_back(42U); // info
+
+ packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
+ packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
+
+ relocations.clear();
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(6U, relocations.size());
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0]));
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5]));
+}
+
+TEST(Delta, Decode32) {
+ decode<ELF32_traits>();
+}
+
+TEST(Delta, Decode64) {
+ decode<ELF64_traits>();
+}
+
+// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC)
+// TODO (dimtiry): 1. Incorrect packed array for decode
+// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc
new file mode 100644
index 0000000..20b25ef
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file.cc
@@ -0,0 +1,882 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation notes:
+//
+// We need to remove a piece from the ELF shared library. However, we also
+// want to avoid fixing DWARF cfi data and relative relocation addresses.
+// So after packing we shift offets and starting address of the RX segment
+// while preserving code/data vaddrs location.
+// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses.
+
+#include "elf_file.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// Out-of-band dynamic tags used to indicate the offset and size of the
+// android packed relocations section.
+static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2;
+static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3;
+
+static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4;
+static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5;
+
+static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1;
+static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2;
+
+// Alignment to preserve, in bytes. This must be at least as large as the
+// largest d_align and sh_addralign values found in the loaded file.
+// Out of caution for RELRO page alignment, we preserve to a complete target
+// page. See http://www.airs.com/blog/archives/189.
+static constexpr size_t kPreserveAlignment = 4096;
+
+// Get section data. Checks that the section has exactly one data entry,
+// so that the section size and the data size are the same. True in
+// practice for all sections we resize when packing or unpacking. Done
+// by ensuring that a call to elf_getdata(section, data) returns NULL as
+// the next data entry.
+static Elf_Data* GetSectionData(Elf_Scn* section) {
+ Elf_Data* data = elf_getdata(section, NULL);
+ CHECK(data && elf_getdata(section, data) == NULL);
+ return data;
+}
+
+// Rewrite section data. Allocates new data and makes it the data element's
+// buffer. Relies on program exit to free allocated data.
+static void RewriteSectionData(Elf_Scn* section,
+ const void* section_data,
+ size_t size) {
+ Elf_Data* data = GetSectionData(section);
+ CHECK(size == data->d_size);
+ uint8_t* area = new uint8_t[size];
+ memcpy(area, section_data, size);
+ data->d_buf = area;
+}
+
+// Verbose ELF header logging.
+template <typename Ehdr>
+static void VerboseLogElfHeader(const Ehdr* elf_header) {
+ VLOG(1) << "e_phoff = " << elf_header->e_phoff;
+ VLOG(1) << "e_shoff = " << elf_header->e_shoff;
+ VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
+ VLOG(1) << "e_phentsize = " << elf_header->e_phentsize;
+ VLOG(1) << "e_phnum = " << elf_header->e_phnum;
+ VLOG(1) << "e_shnum = " << elf_header->e_shnum;
+ VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx;
+}
+
+// Verbose ELF program header logging.
+template <typename Phdr>
+static void VerboseLogProgramHeader(size_t program_header_index,
+ const Phdr* program_header) {
+ std::string type;
+ switch (program_header->p_type) {
+ case PT_NULL: type = "NULL"; break;
+ case PT_LOAD: type = "LOAD"; break;
+ case PT_DYNAMIC: type = "DYNAMIC"; break;
+ case PT_INTERP: type = "INTERP"; break;
+ case PT_PHDR: type = "PHDR"; break;
+ case PT_GNU_RELRO: type = "GNU_RELRO"; break;
+ case PT_GNU_STACK: type = "GNU_STACK"; break;
+ case PT_ARM_EXIDX: type = "EXIDX"; break;
+ default: type = "(OTHER)"; break;
+ }
+ VLOG(1) << "phdr[" << program_header_index << "] : " << type;
+ VLOG(1) << " p_offset = " << program_header->p_offset;
+ VLOG(1) << " p_vaddr = " << program_header->p_vaddr;
+ VLOG(1) << " p_paddr = " << program_header->p_paddr;
+ VLOG(1) << " p_filesz = " << program_header->p_filesz;
+ VLOG(1) << " p_memsz = " << program_header->p_memsz;
+ VLOG(1) << " p_flags = " << program_header->p_flags;
+ VLOG(1) << " p_align = " << program_header->p_align;
+}
+
+// Verbose ELF section header logging.
+template <typename Shdr>
+static void VerboseLogSectionHeader(const std::string& section_name,
+ const Shdr* section_header) {
+ VLOG(1) << "section " << section_name;
+ VLOG(1) << " sh_addr = " << section_header->sh_addr;
+ VLOG(1) << " sh_offset = " << section_header->sh_offset;
+ VLOG(1) << " sh_size = " << section_header->sh_size;
+ VLOG(1) << " sh_entsize = " << section_header->sh_entsize;
+ VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
+}
+
+// Verbose ELF section data logging.
+static void VerboseLogSectionData(const Elf_Data* data) {
+ VLOG(1) << " data";
+ VLOG(1) << " d_buf = " << data->d_buf;
+ VLOG(1) << " d_off = " << data->d_off;
+ VLOG(1) << " d_size = " << data->d_size;
+ VLOG(1) << " d_align = " << data->d_align;
+}
+
+// Load the complete ELF file into a memory image in libelf, and identify
+// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
+// .android.rela.dyn sections. No-op if the ELF file has already been loaded.
+template <typename ELF>
+bool ElfFile<ELF>::Load() {
+ if (elf_)
+ return true;
+
+ Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL);
+ CHECK(elf);
+
+ if (elf_kind(elf) != ELF_K_ELF) {
+ LOG(ERROR) << "File not in ELF format";
+ return false;
+ }
+
+ auto elf_header = ELF::getehdr(elf);
+ if (!elf_header) {
+ LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
+ return false;
+ }
+
+ if (elf_header->e_type != ET_DYN) {
+ LOG(ERROR) << "ELF file is not a shared object";
+ return false;
+ }
+
+ // Require that our endianness matches that of the target, and that both
+ // are little-endian. Safe for all current build/target combinations.
+ const int endian = elf_header->e_ident[EI_DATA];
+ CHECK(endian == ELFDATA2LSB);
+ CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
+
+ const int file_class = elf_header->e_ident[EI_CLASS];
+ VLOG(1) << "endian = " << endian << ", file class = " << file_class;
+ VerboseLogElfHeader(elf_header);
+
+ auto elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header != nullptr);
+
+ const typename ELF::Phdr* dynamic_program_header = NULL;
+ for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+ auto program_header = &elf_program_header[i];
+ VerboseLogProgramHeader(i, program_header);
+
+ if (program_header->p_type == PT_DYNAMIC) {
+ CHECK(dynamic_program_header == NULL);
+ dynamic_program_header = program_header;
+ }
+ }
+ CHECK(dynamic_program_header != nullptr);
+
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ // Notes of the dynamic relocations, packed relocations, and .dynamic
+ // sections. Found while iterating sections, and later stored in class
+ // attributes.
+ Elf_Scn* found_relocations_section = nullptr;
+ Elf_Scn* found_dynamic_section = nullptr;
+
+ // Notes of relocation section types seen. We require one or the other of
+ // these; both is unsupported.
+ bool has_rel_relocations = false;
+ bool has_rela_relocations = false;
+
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != nullptr) {
+ auto section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+ VerboseLogSectionHeader(name, section_header);
+
+ // Note relocation section types.
+ if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) {
+ has_rel_relocations = true;
+ }
+ if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) {
+ has_rela_relocations = true;
+ }
+
+ // Note special sections as we encounter them.
+ if ((name == ".rel.dyn" || name == ".rela.dyn") &&
+ section_header->sh_size > 0) {
+ found_relocations_section = section;
+ }
+
+ if (section_header->sh_offset == dynamic_program_header->p_offset) {
+ found_dynamic_section = section;
+ }
+
+ // Ensure we preserve alignment, repeated later for the data block(s).
+ CHECK(section_header->sh_addralign <= kPreserveAlignment);
+
+ Elf_Data* data = NULL;
+ while ((data = elf_getdata(section, data)) != NULL) {
+ CHECK(data->d_align <= kPreserveAlignment);
+ VerboseLogSectionData(data);
+ }
+ }
+
+ // Loading failed if we did not find the required special sections.
+ if (!found_relocations_section) {
+ LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
+ return false;
+ }
+ if (!found_dynamic_section) {
+ LOG(ERROR) << "Missing .dynamic section";
+ return false;
+ }
+
+ // Loading failed if we could not identify the relocations type.
+ if (!has_rel_relocations && !has_rela_relocations) {
+ LOG(ERROR) << "No relocations sections found";
+ return false;
+ }
+ if (has_rel_relocations && has_rela_relocations) {
+ LOG(ERROR) << "Multiple relocations sections with different types found, "
+ << "not currently supported";
+ return false;
+ }
+
+ elf_ = elf;
+ relocations_section_ = found_relocations_section;
+ dynamic_section_ = found_dynamic_section;
+ relocations_type_ = has_rel_relocations ? REL : RELA;
+ return true;
+}
+
+// Helper for ResizeSection(). Adjust the main ELF header for the hole.
+template <typename ELF>
+static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ if (elf_header->e_phoff > hole_start) {
+ elf_header->e_phoff += hole_size;
+ VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
+ }
+ if (elf_header->e_shoff > hole_start) {
+ elf_header->e_shoff += hole_size;
+ VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff;
+ }
+}
+
+// Helper for ResizeSection(). Adjust all section headers for the hole.
+template <typename ELF>
+static void AdjustSectionHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ auto section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+ if (section_header->sh_offset > hole_start) {
+ section_header->sh_offset += hole_size;
+ VLOG(1) << "section " << name
+ << " sh_offset adjusted to " << section_header->sh_offset;
+ } else {
+ section_header->sh_addr -= hole_size;
+ VLOG(1) << "section " << name
+ << " sh_addr adjusted to " << section_header->sh_addr;
+ }
+ }
+}
+
+// Helper for ResizeSection(). Adjust the offsets of any program headers
+// that have offsets currently beyond the hole start.
+template <typename ELF>
+static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ for (size_t i = 0; i < count; ++i) {
+ typename ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header->p_offset > hole_start) {
+ // The hole start is past this segment, so adjust offset.
+ program_header->p_offset += hole_size;
+ VLOG(1) << "phdr[" << i
+ << "] p_offset adjusted to "<< program_header->p_offset;
+ } else {
+ program_header->p_vaddr -= hole_size;
+ program_header->p_paddr -= hole_size;
+ VLOG(1) << "phdr[" << i
+ << "] p_vaddr adjusted to "<< program_header->p_vaddr
+ << "; p_paddr adjusted to "<< program_header->p_paddr;
+ }
+ }
+}
+
+// Helper for ResizeSection(). Find the first loadable segment in the
+// file. We expect it to map from file offset zero.
+template <typename ELF>
+static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start) {
+ for (size_t i = 0; i < count; ++i) {
+ typename ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header->p_type == PT_LOAD &&
+ program_header->p_offset <= hole_start &&
+ (program_header->p_offset + program_header->p_filesz) >= hole_start ) {
+ return program_header;
+ }
+ }
+ LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start;
+ NOTREACHED();
+
+ return nullptr;
+}
+
+// Helper for ResizeSection(). Rewrite program headers.
+template <typename ELF>
+static void RewriteProgramHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ CHECK(elf_header);
+
+ typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ const size_t program_header_count = elf_header->e_phnum;
+
+ // Locate the segment that we can overwrite to form the new LOAD entry,
+ // and the segment that we are going to split into two parts.
+ typename ELF::Phdr* target_load_header =
+ FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start);
+
+ VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust";
+ // Adjust PT_LOAD program header memsz and filesz
+ target_load_header->p_filesz += hole_size;
+ target_load_header->p_memsz += hole_size;
+
+ // Adjust the offsets and p_vaddrs
+ AdjustProgramHeaderOffsets<ELF>(elf_program_header,
+ program_header_count,
+ hole_start,
+ hole_size);
+}
+
+// Helper for ResizeSection(). Locate and return the dynamic section.
+template <typename ELF>
+static Elf_Scn* GetDynamicSection(Elf* elf) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ CHECK(elf_header);
+
+ const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ // Find the program header that describes the dynamic section.
+ const typename ELF::Phdr* dynamic_program_header = NULL;
+ for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+ const typename ELF::Phdr* program_header = &elf_program_header[i];
+
+ if (program_header->p_type == PT_DYNAMIC) {
+ dynamic_program_header = program_header;
+ }
+ }
+ CHECK(dynamic_program_header);
+
+ // Now find the section with the same offset as this program header.
+ Elf_Scn* dynamic_section = NULL;
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ typename ELF::Shdr* section_header = ELF::getshdr(section);
+
+ if (section_header->sh_offset == dynamic_program_header->p_offset) {
+ dynamic_section = section;
+ }
+ }
+ CHECK(dynamic_section != NULL);
+
+ return dynamic_section;
+}
+
+// Helper for ResizeSection(). Adjust the .dynamic section for the hole.
+template <typename ELF>
+void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+ typename ELF::Off hole_start,
+ ssize_t hole_size,
+ relocations_type_t relocations_type) {
+ CHECK(relocations_type != NONE);
+ Elf_Data* data = GetSectionData(dynamic_section);
+
+ auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+
+ if (hole_size > 0) { // expanding
+ hole_start += hole_size;
+ }
+
+ for (size_t i = 0; i < dynamics.size(); ++i) {
+ typename ELF::Dyn* dynamic = &dynamics[i];
+ const typename ELF::Sword tag = dynamic->d_tag;
+
+ // Any tags that hold offsets are adjustment candidates.
+ const bool is_adjustable = (tag == DT_PLTGOT ||
+ tag == DT_HASH ||
+ tag == DT_GNU_HASH ||
+ tag == DT_STRTAB ||
+ tag == DT_SYMTAB ||
+ tag == DT_RELA ||
+ tag == DT_INIT ||
+ tag == DT_FINI ||
+ tag == DT_REL ||
+ tag == DT_JMPREL ||
+ tag == DT_INIT_ARRAY ||
+ tag == DT_FINI_ARRAY ||
+ tag == DT_ANDROID_REL||
+ tag == DT_ANDROID_RELA);
+
+ if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) {
+ dynamic->d_un.d_ptr -= hole_size;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_ptr adjusted to " << dynamic->d_un.d_ptr;
+ }
+
+ // DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
+ // Only one will be present. Adjust by hole size.
+ if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) {
+ dynamic->d_un.d_val += hole_size;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_val adjusted to " << dynamic->d_un.d_val;
+ }
+
+ // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and
+ // technically (2) the relative relocation count is not changed.
+
+ // DT_RELENT and DT_RELAENT don't change, ignore them as well.
+ }
+
+ void* section_data = &dynamics[0];
+ size_t bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(dynamic_section, section_data, bytes);
+}
+
+// Resize a section. If the new size is larger than the current size, open
+// up a hole by increasing file offsets that come after the hole. If smaller
+// than the current size, remove the hole by decreasing those offsets.
+template <typename ELF>
+void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+ typename ELF::Word new_sh_type,
+ relocations_type_t relocations_type) {
+
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+ auto section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+ if (section_header->sh_size == new_size) {
+ return;
+ }
+
+ // Require that the section size and the data size are the same. True
+ // in practice for all sections we resize when packing or unpacking.
+ Elf_Data* data = GetSectionData(section);
+ CHECK(data->d_off == 0 && data->d_size == section_header->sh_size);
+
+ // Require that the section is not zero-length (that is, has allocated
+ // data that we can validly expand).
+ CHECK(data->d_size && data->d_buf);
+
+ const auto hole_start = section_header->sh_offset;
+ const ssize_t hole_size = new_size - data->d_size;
+
+ VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+ VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+
+ // libelf overrides sh_entsize for known sh_types, so it does not matter what we set
+ // for SHT_REL/SHT_RELA.
+ typename ELF::Xword new_entsize =
+ (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0;
+
+ VLOG(1) << "Update section (" << name << ") entry size: " <<
+ section_header->sh_entsize << " -> " << new_entsize;
+
+ // Resize the data and the section header.
+ data->d_size += hole_size;
+ section_header->sh_size += hole_size;
+ section_header->sh_entsize = new_entsize;
+ section_header->sh_type = new_sh_type;
+
+ // Add the hole size to all offsets in the ELF file that are after the
+ // start of the hole. If the hole size is positive we are expanding the
+ // section to create a new hole; if negative, we are closing up a hole.
+
+ // Start with the main ELF header.
+ typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size);
+
+ // Adjust all section headers.
+ AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size);
+
+ // Rewrite the program headers to either split or coalesce segments,
+ // and adjust dynamic entries to match.
+ RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size);
+
+ Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf);
+ AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type);
+}
+
+// Find the first slot in a dynamics array with the given tag. The array
+// always ends with a free (unused) element, and which we exclude from the
+// search. Returns dynamics->size() if not found.
+template <typename ELF>
+static size_t FindDynamicEntry(typename ELF::Sword tag,
+ std::vector<typename ELF::Dyn>* dynamics) {
+ // Loop until the penultimate entry. We exclude the end sentinel.
+ for (size_t i = 0; i < dynamics->size() - 1; ++i) {
+ if (dynamics->at(i).d_tag == tag) {
+ return i;
+ }
+ }
+
+ // The tag was not found.
+ return dynamics->size();
+}
+
+// Replace dynamic entry.
+template <typename ELF>
+static void ReplaceDynamicEntry(typename ELF::Sword tag,
+ const typename ELF::Dyn& dyn,
+ std::vector<typename ELF::Dyn>* dynamics) {
+ const size_t slot = FindDynamicEntry<ELF>(tag, dynamics);
+ if (slot == dynamics->size()) {
+ LOG(FATAL) << "Dynamic slot is not found for tag=" << tag;
+ }
+
+ // Replace this entry with the one supplied.
+ dynamics->at(slot) = dyn;
+ VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
+}
+
+// Remove relative entries from dynamic relocations and write as packed
+// data into android packed relocations.
+template <typename ELF>
+bool ElfFile<ELF>::PackRelocations() {
+ // Load the ELF file into libelf.
+ if (!Load()) {
+ LOG(ERROR) << "Failed to load as ELF";
+ return false;
+ }
+
+ // Retrieve the current dynamic relocations section data.
+ Elf_Data* data = GetSectionData(relocations_section_);
+ // we always pack rela, because packed format is pretty much the same
+ std::vector<typename ELF::Rela> relocations;
+
+ if (relocations_type_ == REL) {
+ // Convert data to a vector of relocations.
+ const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf);
+ ConvertRelArrayToRelaVector(relocations_base,
+ data->d_size / sizeof(typename ELF::Rel), &relocations);
+ LOG(INFO) << "Relocations : REL";
+ } else if (relocations_type_ == RELA) {
+ // Convert data to a vector of relocations with addends.
+ const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf);
+ relocations = std::vector<typename ELF::Rela>(
+ relocations_base,
+ relocations_base + data->d_size / sizeof(relocations[0]));
+
+ LOG(INFO) << "Relocations : RELA";
+ } else {
+ NOTREACHED();
+ }
+
+ return PackTypedRelocations(&relocations);
+}
+
+// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
+template <typename ELF>
+bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) {
+ typedef typename ELF::Rela Rela;
+
+ // If no relocations then we have nothing packable. Perhaps
+ // the shared object has already been packed?
+ if (relocations->empty()) {
+ LOG(ERROR) << "No relocations found (already packed?)";
+ return false;
+ }
+
+ const size_t rel_size =
+ relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel);
+ const size_t initial_bytes = relocations->size() * rel_size;
+
+ LOG(INFO) << "Unpacked : " << initial_bytes << " bytes";
+ std::vector<uint8_t> packed;
+ RelocationPacker<ELF> packer;
+
+ // Pack relocations: dry run to estimate memory savings.
+ packer.PackRelocations(*relocations, &packed);
+ const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes";
+
+ if (packed.empty()) {
+ LOG(INFO) << "Too few relocations to pack";
+ return false;
+ }
+
+ // Pre-calculate the size of the hole we will close up when we rewrite
+ // dynamic relocations. We have to adjust relocation addresses to
+ // account for this.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+ ssize_t hole_size = initial_bytes - packed_bytes_estimate;
+
+ // hole_size needs to be page_aligned.
+ hole_size -= hole_size % kPreserveAlignment;
+
+ LOG(INFO) << "Compaction : " << hole_size << " bytes";
+
+ // Adjusting for alignment may have removed any packing benefit.
+ if (hole_size == 0) {
+ LOG(INFO) << "Too few relocations to pack after alignment";
+ return false;
+ }
+
+ if (hole_size <= 0) {
+ LOG(INFO) << "Packing relocations saves no space";
+ return false;
+ }
+
+ size_t data_padding_bytes = is_padding_relocations_ ?
+ initial_bytes - packed_bytes_estimate :
+ initial_bytes - hole_size - packed_bytes_estimate;
+
+ // pad data
+ std::vector<uint8_t> padding(data_padding_bytes, 0);
+ packed.insert(packed.end(), padding.begin(), padding.end());
+
+ const void* packed_data = &packed[0];
+
+ // Run a loopback self-test as a check that packing is lossless.
+ std::vector<Rela> unpacked;
+ packer.UnpackRelocations(packed, &unpacked);
+ CHECK(unpacked.size() == relocations->size());
+ CHECK(!memcmp(&unpacked[0],
+ &relocations->at(0),
+ unpacked.size() * sizeof(unpacked[0])));
+
+ // Rewrite the current dynamic relocations section with packed one then shrink it to size.
+ const size_t bytes = packed.size() * sizeof(packed[0]);
+ ResizeSection(elf_, relocations_section_, bytes,
+ relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, packed_data, bytes);
+
+ // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt
+
+ // Rewrite .dynamic and rename relocation tags describing the packed android
+ // relocations.
+ Elf_Data* data = GetSectionData(dynamic_section_);
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+ section_header = ELF::getshdr(relocations_section_);
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics);
+ }
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ;
+ dyn.d_un.d_val = section_header->sh_size;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics);
+ }
+
+ const void* dynamics_data = &dynamics[0];
+ const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
+
+ Flush();
+ return true;
+}
+
+// Find packed relative relocations in the packed android relocations
+// section, unpack them, and rewrite the dynamic relocations section to
+// contain unpacked data.
+template <typename ELF>
+bool ElfFile<ELF>::UnpackRelocations() {
+ // Load the ELF file into libelf.
+ if (!Load()) {
+ LOG(ERROR) << "Failed to load as ELF";
+ return false;
+ }
+
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+ // Retrieve the current packed android relocations section data.
+ Elf_Data* data = GetSectionData(relocations_section_);
+
+ // Convert data to a vector of bytes.
+ const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
+ std::vector<uint8_t> packed(
+ packed_base,
+ packed_base + data->d_size / sizeof(packed[0]));
+
+ if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) &&
+ packed.size() > 3 &&
+ packed[0] == 'A' &&
+ packed[1] == 'P' &&
+ (packed[2] == 'U' || packed[2] == 'S') &&
+ packed[3] == '2') {
+ LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA");
+ } else {
+ LOG(ERROR) << "Packed relocations not found (not packed?)";
+ return false;
+ }
+
+ return UnpackTypedRelocations(packed);
+}
+
+// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
+template <typename ELF>
+bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
+ // Unpack the data to re-materialize the relative relocations.
+ const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed : " << packed_bytes << " bytes";
+ std::vector<typename ELF::Rela> unpacked_relocations;
+ RelocationPacker<ELF> packer;
+ packer.UnpackRelocations(packed, &unpacked_relocations);
+
+ const size_t relocation_entry_size =
+ relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela);
+ const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size;
+ LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes";
+
+ // Retrieve the current dynamic relocations section data.
+ Elf_Data* data = GetSectionData(relocations_section_);
+
+ LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries";
+
+ // If we found the same number of null relocation entries in the dynamic
+ // relocations section as we hold as unpacked relative relocations, then
+ // this is a padded file.
+
+ const bool is_padded = packed_bytes == unpacked_bytes;
+
+ // Unless padded, pre-apply relative relocations to account for the
+ // hole, and pre-adjust all relocation offsets accordingly.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+
+ if (!is_padded) {
+ LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes";
+ }
+
+ // Rewrite the current dynamic relocations section with unpacked version of
+ // relocations.
+ const void* section_data = nullptr;
+ std::vector<typename ELF::Rel> unpacked_rel_relocations;
+ if (relocations_type_ == RELA) {
+ section_data = &unpacked_relocations[0];
+ } else if (relocations_type_ == REL) {
+ ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations);
+ section_data = &unpacked_rel_relocations[0];
+ } else {
+ NOTREACHED();
+ }
+
+ ResizeSection(elf_, relocations_section_, unpacked_bytes,
+ relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, section_data, unpacked_bytes);
+
+ // Rewrite .dynamic to remove two tags describing packed android relocations.
+ data = GetSectionData(dynamic_section_);
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA,
+ dyn, &dynamics);
+ }
+
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ;
+ dyn.d_un.d_val = section_header->sh_size;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ,
+ dyn, &dynamics);
+ }
+
+ const void* dynamics_data = &dynamics[0];
+ const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
+
+ Flush();
+ return true;
+}
+
+// Flush rewritten shared object file data.
+template <typename ELF>
+void ElfFile<ELF>::Flush() {
+ // Flag all ELF data held in memory as needing to be written back to the
+ // file, and tell libelf that we have controlled the file layout.
+ elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
+ elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT);
+
+ // Write ELF data back to disk.
+ const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
+ if (file_bytes == -1) {
+ LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno());
+ }
+
+ CHECK(file_bytes > 0);
+ VLOG(1) << "elf_update returned: " << file_bytes;
+
+ // Clean up libelf, and truncate the output file to the number of bytes
+ // written by elf_update().
+ elf_end(elf_);
+ elf_ = NULL;
+ const int truncate = ftruncate(fd_, file_bytes);
+ CHECK(truncate == 0);
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array,
+ size_t rel_array_size,
+ std::vector<typename ELF::Rela>* rela_vector) {
+ for (size_t i = 0; i<rel_array_size; ++i) {
+ typename ELF::Rela rela;
+ rela.r_offset = rel_array[i].r_offset;
+ rela.r_info = rel_array[i].r_info;
+ rela.r_addend = 0;
+ rela_vector->push_back(rela);
+ }
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+ std::vector<typename ELF::Rel>* rel_vector) {
+ for (auto rela : rela_vector) {
+ typename ELF::Rel rel;
+ rel.r_offset = rela.r_offset;
+ rel.r_info = rela.r_info;
+ CHECK(rela.r_addend == 0);
+ rel_vector->push_back(rel);
+ }
+}
+
+template class ElfFile<ELF32_traits>;
+template class ElfFile<ELF64_traits>;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h
new file mode 100644
index 0000000..73c3192
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file.h
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ELF shared object file updates handler.
+//
+// Provides functions to remove relative relocations from the .rel.dyn
+// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn,
+// and unpack to return the file to its pre-packed state.
+//
+// Files to be packed or unpacked must include an existing .android.rel.dyn
+// or android.rela.dyn section. A standard libchrome.<version>.so will not
+// contain this section, so the following can be used to add one:
+//
+// echo -n 'NULL' >/tmp/small
+// if file libchrome.<version>.so | grep -q 'ELF 32'; then
+// arm-linux-androideabi-objcopy
+// --add-section .android.rel.dyn=/tmp/small
+// libchrome.<version>.so libchrome.<version>.so.packed
+// else
+// aarch64-linux-android-objcopy
+// --add-section .android.rela.dyn=/tmp/small
+// libchrome.<version>.so libchrome.<version>.so.packed
+// fi
+// rm /tmp/small
+//
+// To use, open the file and pass the file descriptor to the constructor,
+// then pack or unpack as desired. Packing or unpacking will flush the file
+// descriptor on success. Example:
+//
+// int fd = open(..., O_RDWR);
+// ElfFile elf_file(fd);
+// bool status;
+// if (is_packing)
+// status = elf_file.PackRelocations();
+// else
+// status = elf_file.UnpackRelocations();
+// close(fd);
+//
+// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with
+// NONE-type entries rather than cutting a hole out of the shared object
+// file. This keeps all load addresses and offsets constant, and enables
+// easier debugging and testing.
+//
+// A packed shared object file has all of its relative relocations
+// removed from .rel.dyn or .rela.dyn, and replaced as packed data in
+// .android.rel.dyn or .android.rela.dyn respectively. The resulting file
+// is shorter than its non-packed original.
+//
+// Unpacking a packed file restores the file to its non-packed state, by
+// expanding the packed data in .android.rel.dyn or .android.rela.dyn,
+// combining the relative relocations with the data already in .rel.dyn
+// or .rela.dyn, and then writing back the now expanded section.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+
+#include <string.h>
+#include <vector>
+
+#include "elf.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// An ElfFile reads shared objects, and shuttles relative relocations
+// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
+// sections.
+template <typename ELF>
+class ElfFile {
+ public:
+ explicit ElfFile(int fd)
+ : fd_(fd), is_padding_relocations_(false), elf_(NULL),
+ relocations_section_(NULL), dynamic_section_(NULL),
+ relocations_type_(NONE) {}
+ ~ElfFile() {}
+
+ // Set padding mode. When padding, PackRelocations() will not shrink
+ // the .rel.dyn or .rela.dyn section, but instead replace relative with
+ // NONE-type entries.
+ // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it.
+ inline void SetPadding(bool flag) { is_padding_relocations_ = flag; }
+
+ // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed
+ // representation in .android.rel.dyn or .android.rela.dyn. Returns true
+ // on success.
+ bool PackRelocations();
+
+ // Transfer relative relocations from a packed representation in
+ // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn. Returns
+ // true on success.
+ bool UnpackRelocations();
+
+ private:
+ enum relocations_type_t {
+ NONE = 0, REL, RELA
+ };
+
+ // Load a new ElfFile from a filedescriptor. If flushing, the file must
+ // be open for read/write. Returns true on successful ELF file load.
+ // |fd| is an open file descriptor for the shared object.
+ bool Load();
+
+ // Templated packer, helper for PackRelocations(). Rel type is one of
+ // ELF::Rel or ELF::Rela.
+ bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
+
+ // Templated unpacker, helper for UnpackRelocations(). Rel type is one of
+ // ELF::Rel or ELF::Rela.
+ bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
+
+ // Write ELF file changes.
+ void Flush();
+
+ void AdjustRelativeRelocationTargets(typename ELF::Off hole_start,
+ ssize_t hole_size,
+ std::vector<typename ELF::Rela>* relocations);
+
+ static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+ typename ELF::Word new_sh_type, relocations_type_t relocations_type);
+
+ static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+ typename ELF::Off hole_start,
+ ssize_t hole_size,
+ relocations_type_t relocations_type);
+
+ static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size,
+ std::vector<typename ELF::Rela>* rela_vector);
+
+ static void ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+ std::vector<typename ELF::Rel>* rel_vector);
+
+
+ // File descriptor opened on the shared object.
+ int fd_;
+
+ // If set, pad rather than shrink .rel.dyn or .rela.dyn. Primarily for
+ // debugging, allows packing to be checked without affecting load addresses.
+ bool is_padding_relocations_;
+
+ // Libelf handle, assigned by Load().
+ Elf* elf_;
+
+ // Sections that we manipulate, assigned by Load().
+ Elf_Scn* relocations_section_;
+ Elf_Scn* dynamic_section_;
+
+ // Relocation type found, assigned by Load().
+ relocations_type_t relocations_type_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc
new file mode 100644
index 0000000..434f101
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "elf_file.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "debug.h"
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+void GetDataFilePath(const char* name, std::string* path) {
+ std::string data_dir;
+
+ const char* bindir = getenv("bindir");
+ if (bindir) {
+ data_dir = std::string(bindir);
+ } else {
+ char path[PATH_MAX];
+ memset(path, 0, sizeof(path));
+ ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1));
+
+ data_dir = std::string(path);
+ size_t pos = data_dir.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+
+ data_dir.erase(pos);
+ }
+
+ *path = data_dir + "/" + name;
+}
+
+void OpenRelocsTestFile(const char* name, FILE** stream) {
+ std::string path;
+ GetDataFilePath(name, &path);
+
+ FILE* testfile = fopen(path.c_str(), "rb");
+ ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'";
+
+ FILE* temporary = tmpfile();
+ ASSERT_FALSE(temporary == NULL);
+
+ static const size_t buffer_size = 4096;
+ unsigned char buffer[buffer_size];
+
+ size_t bytes;
+ do {
+ bytes = fread(buffer, 1, sizeof(buffer), testfile);
+ ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary));
+ } while (bytes > 0);
+
+ ASSERT_EQ(0, fclose(testfile));
+ ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET));
+
+ *stream = temporary;
+}
+
+void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) {
+ const std::string base = std::string("elf_file_unittest_relocs_") + arch;
+ const std::string relocs = base + ".so";
+ const std::string packed_relocs = base + "_packed.so";
+
+ OpenRelocsTestFile(relocs.c_str(), relocs_so);
+ OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so);
+}
+
+void CloseRelocsTestFile(FILE* temporary) {
+ fclose(temporary);
+}
+
+void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) {
+ CloseRelocsTestFile(relocs_so);
+ CloseRelocsTestFile(packed_relocs_so);
+}
+
+void CheckFileContentsEqual(FILE* first, FILE* second) {
+ ASSERT_EQ(0, fseek(first, 0, SEEK_SET));
+ ASSERT_EQ(0, fseek(second, 0, SEEK_SET));
+
+ static const size_t buffer_size = 4096;
+ unsigned char first_buffer[buffer_size];
+ unsigned char second_buffer[buffer_size];
+
+ do {
+ size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first);
+ size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second);
+
+ EXPECT_EQ(first_read, second_read);
+ EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read));
+ } while (!feof(first) && !feof(second));
+
+ EXPECT_TRUE(feof(first) && feof(second));
+}
+
+template <typename ELF>
+static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
+ relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
+
+ // Ensure packing fails (already packed).
+ EXPECT_FALSE(elf_file.PackRelocations());
+
+ // Unpack golden relocations, and check files are now identical.
+ EXPECT_TRUE(elf_file.UnpackRelocations());
+ CheckFileContentsEqual(packed_relocs_so, relocs_so);
+
+ CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+static void RunUnpackRelocationsTestFor(const std::string& arch) {
+ ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
+
+ FILE* relocs_so = NULL;
+ FILE* packed_relocs_so = NULL;
+ OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
+
+ if (relocs_so != NULL && packed_relocs_so != NULL) {
+ // lets detect elf class
+ ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET))
+ << "Invalid file length: " << strerror(errno);
+ uint8_t elf_class = 0;
+ ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so));
+ ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET));
+ if (elf_class == ELFCLASS32) {
+ ProcessUnpack<ELF32_traits>(relocs_so, packed_relocs_so);
+ } else {
+ ProcessUnpack<ELF64_traits>(relocs_so, packed_relocs_so);
+ }
+ }
+}
+
+template <typename ELF>
+static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) {
+ relocation_packer::ElfFile<ELF> elf_file(fileno(relocs_so));
+
+ // Ensure unpacking fails (not packed).
+ EXPECT_FALSE(elf_file.UnpackRelocations());
+
+ // Pack relocations, and check files are now identical.
+ EXPECT_TRUE(elf_file.PackRelocations());
+ CheckFileContentsEqual(relocs_so, packed_relocs_so);
+
+ CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+static void RunPackRelocationsTestFor(const std::string& arch) {
+ ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
+
+ FILE* relocs_so = NULL;
+ FILE* packed_relocs_so = NULL;
+ OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
+
+ if (relocs_so != NULL && packed_relocs_so != NULL) {
+ // lets detect elf class
+ ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET))
+ << "Invalid file length: " << strerror(errno);
+ uint8_t elf_class = 0;
+ ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so));
+ fseek(packed_relocs_so, 0, SEEK_SET);
+ if (elf_class == ELFCLASS32) {
+ ProcessPack<ELF32_traits>(relocs_so, packed_relocs_so);
+ } else {
+ ProcessPack<ELF64_traits>(relocs_so, packed_relocs_so);
+ }
+ }
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+TEST(ElfFile, PackRelocations) {
+ RunPackRelocationsTestFor("arm32");
+ RunPackRelocationsTestFor("arm64");
+}
+
+TEST(ElfFile, UnpackRelocations) {
+ RunUnpackRelocationsTestFor("arm32");
+ RunUnpackRelocationsTestFor("arm64");
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h
new file mode 100644
index 0000000..41b06c8
--- /dev/null
+++ b/tools/relocation_packer/src/elf_traits.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Target-specific ELF type traits.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+
+#include "elf.h"
+#include "libelf.h"
+
+// ELF is a traits structure used to provide convenient aliases for
+// 32/64 bit Elf types and functions, depending on the target file.
+
+struct ELF32_traits {
+ typedef Elf32_Addr Addr;
+ typedef Elf32_Dyn Dyn;
+ typedef Elf32_Ehdr Ehdr;
+ typedef Elf32_Off Off;
+ typedef Elf32_Phdr Phdr;
+ typedef Elf32_Rel Rel;
+ typedef Elf32_Rela Rela;
+ typedef Elf32_Shdr Shdr;
+ typedef Elf32_Sword Sword;
+ typedef Elf32_Sxword Sxword;
+ typedef Elf32_Sym Sym;
+ typedef Elf32_Word Word;
+ typedef Elf32_Xword Xword;
+ typedef Elf32_Half Half;
+
+ static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
+ static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
+ static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
+ static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
+ static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
+ static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
+};
+
+struct ELF64_traits {
+ typedef Elf64_Addr Addr;
+ typedef Elf64_Dyn Dyn;
+ typedef Elf64_Ehdr Ehdr;
+ typedef Elf64_Off Off;
+ typedef Elf64_Phdr Phdr;
+ typedef Elf64_Rel Rel;
+ typedef Elf64_Rela Rela;
+ typedef Elf64_Shdr Shdr;
+ typedef Elf64_Sword Sword;
+ typedef Elf64_Sxword Sxword;
+ typedef Elf64_Sym Sym;
+ typedef Elf64_Word Word;
+ typedef Elf64_Xword Xword;
+ typedef Elf64_Half Half;
+
+ static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
+ static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
+ static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
+ static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
+ static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
+ static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
+};
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc
new file mode 100644
index 0000000..101c557
--- /dev/null
+++ b/tools/relocation_packer/src/leb128.cc
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+template <typename uint_t>
+Leb128Encoder<uint_t>::Leb128Encoder() { }
+
+template <typename uint_t>
+Leb128Encoder<uint_t>::~Leb128Encoder() { }
+
+// Add a single value to the encoding. Values are encoded with variable
+// length. The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last.
+template <typename uint_t>
+void Leb128Encoder<uint_t>::Enqueue(uint_t value) {
+ uint_t uvalue = static_cast<uint_t>(value);
+ do {
+ const uint8_t byte = uvalue & 127;
+ uvalue >>= 7;
+ encoding_.push_back((uvalue ? 128 : 0) | byte);
+ } while (uvalue);
+}
+
+// Add a vector of values to the encoding.
+template <typename uint_t>
+void Leb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
+ for (size_t i = 0; i < values.size(); ++i) {
+ Enqueue(values[i]);
+ }
+}
+
+// Create a new decoder for the given encoded stream.
+template <typename uint_t>
+Leb128Decoder<uint_t>::Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
+ encoding_ = encoding;
+ cursor_ = start_with;
+}
+
+// Empty destructor to silence chromium-style.
+template <typename uint_t>
+Leb128Decoder<uint_t>::~Leb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding. Read forwards until
+// a byte without its most significant bit is found, then read the 7 bit
+// fields of the bytes spanned to re-form the value.
+template <typename uint_t>
+uint_t Leb128Decoder<uint_t>::Dequeue() {
+ uint_t value = 0;
+
+ size_t shift = 0;
+ uint8_t byte;
+
+ // Loop until we reach a byte with its high order bit clear.
+ do {
+ byte = encoding_[cursor_++];
+ value |= static_cast<uint_t>(byte & 127) << shift;
+ shift += 7;
+ } while (byte & 128);
+
+ return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+template <typename uint_t>
+void Leb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
+ while (cursor_ < encoding_.size()) {
+ values->push_back(Dequeue());
+ }
+}
+
+template class Leb128Encoder<uint32_t>;
+template class Leb128Encoder<uint64_t>;
+
+template class Leb128Decoder<uint32_t>;
+template class Leb128Decoder<uint64_t>;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h
new file mode 100644
index 0000000..2c5b5d0
--- /dev/null
+++ b/tools/relocation_packer/src/leb128.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// LEB128 encoder and decoder for packed relative relocations.
+//
+// Run-length encoded relative relocations consist of a large number
+// of pairs of relatively small positive integer values. Encoding these as
+// LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a LEB128 byte stream.
+template <typename uint_t>
+class Leb128Encoder {
+ public:
+ // Explicit (but empty) constructor and destructor, for chromium-style.
+ Leb128Encoder();
+ ~Leb128Encoder();
+
+ // Add a value to the encoding stream.
+ // |value| is the unsigned int to add.
+ void Enqueue(uint_t value);
+
+ // Add a vector of values to the encoding stream.
+ // |values| is the vector of unsigned ints to add.
+ void EnqueueAll(const std::vector<uint_t>& values);
+
+ // Retrieve the encoded representation of the values.
+ // |encoding| is the returned vector of encoded data.
+ void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+ // Growable vector holding the encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+template <typename uint_t>
+class Leb128Decoder {
+ public:
+ // Create a new decoder for the given encoded stream.
+ // |encoding| is the vector of encoded data.
+ explicit Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
+
+ // Explicit (but empty) destructor, for chromium-style.
+ ~Leb128Decoder();
+
+ // Retrieve the next value from the encoded stream.
+ uint_t Dequeue();
+
+ // Retrieve all remaining values from the encoded stream.
+ // |values| is the vector of decoded data.
+ void DequeueAll(std::vector<uint_t>* values);
+
+ private:
+ // Encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+
+ // Cursor indicating the current stream retrieval point.
+ size_t cursor_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc
new file mode 100644
index 0000000..8a7028c
--- /dev/null
+++ b/tools/relocation_packer/src/leb128_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <vector>
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Leb128, Encoder64) {
+ std::vector<uint64_t> values;
+ values.push_back(624485);
+ values.push_back(0);
+ values.push_back(1);
+ values.push_back(127);
+ values.push_back(128);
+
+ Leb128Encoder<uint64_t> encoder;
+ encoder.EnqueueAll(values);
+
+ encoder.Enqueue(4294967295);
+ encoder.Enqueue(18446744073709551615ul);
+
+ std::vector<uint8_t> encoding;
+ encoder.GetEncoding(&encoding);
+
+ EXPECT_EQ(23U, encoding.size());
+ // 624485
+ EXPECT_EQ(0xe5, encoding[0]);
+ EXPECT_EQ(0x8e, encoding[1]);
+ EXPECT_EQ(0x26, encoding[2]);
+ // 0
+ EXPECT_EQ(0x00, encoding[3]);
+ // 1
+ EXPECT_EQ(0x01, encoding[4]);
+ // 127
+ EXPECT_EQ(0x7f, encoding[5]);
+ // 128
+ EXPECT_EQ(0x80, encoding[6]);
+ EXPECT_EQ(0x01, encoding[7]);
+ // 4294967295
+ EXPECT_EQ(0xff, encoding[8]);
+ EXPECT_EQ(0xff, encoding[9]);
+ EXPECT_EQ(0xff, encoding[10]);
+ EXPECT_EQ(0xff, encoding[11]);
+ EXPECT_EQ(0x0f, encoding[12]);
+ // 18446744073709551615
+ EXPECT_EQ(0xff, encoding[13]);
+ EXPECT_EQ(0xff, encoding[14]);
+ EXPECT_EQ(0xff, encoding[15]);
+ EXPECT_EQ(0xff, encoding[16]);
+ EXPECT_EQ(0xff, encoding[17]);
+ EXPECT_EQ(0xff, encoding[18]);
+ EXPECT_EQ(0xff, encoding[19]);
+ EXPECT_EQ(0xff, encoding[20]);
+ EXPECT_EQ(0xff, encoding[21]);
+ EXPECT_EQ(0x01, encoding[22]);
+}
+
+TEST(Leb128, Decoder64) {
+ 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);
+ // 127
+ encoding.push_back(0x7f);
+ // 128
+ encoding.push_back(0x80);
+ encoding.push_back(0x01);
+ // 4294967295
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0x0f);
+ // 18446744073709551615
+ 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(0x01);
+
+ Leb128Decoder<uint64_t> decoder(encoding, 0);
+
+ EXPECT_EQ(624485U, decoder.Dequeue());
+
+ std::vector<uint64_t> dequeued;
+ decoder.DequeueAll(&dequeued);
+
+ EXPECT_EQ(6U, dequeued.size());
+ EXPECT_EQ(0U, dequeued[0]);
+ EXPECT_EQ(1U, dequeued[1]);
+ EXPECT_EQ(127U, dequeued[2]);
+ EXPECT_EQ(128U, dequeued[3]);
+ EXPECT_EQ(4294967295U, dequeued[4]);
+ EXPECT_EQ(18446744073709551615UL, dequeued[5]);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc
new file mode 100644
index 0000000..3f784e4
--- /dev/null
+++ b/tools/relocation_packer/src/main.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tool to pack and unpack relative relocations in a shared library.
+//
+// Packing removes relative relocations from .rel.dyn and writes them
+// in a more compact form to .android.rel.dyn. Unpacking does the reverse.
+//
+// Invoke with -v to trace actions taken when packing or unpacking.
+// Invoke with -p to pad removed relocations with R_*_NONE. Suppresses
+// shrinking of .rel.dyn.
+// See PrintUsage() below for full usage details.
+//
+// NOTE: Breaks with libelf 0.152, which is buggy. libelf 0.158 works.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#include "debug.h"
+#include "elf_file.h"
+#include "elf_traits.h"
+#include "libelf.h"
+
+#include "nativehelper/ScopedFd.h"
+
+static void PrintUsage(const char* argv0) {
+ std::string temporary = argv0;
+ const size_t last_slash = temporary.find_last_of("/");
+ if (last_slash != temporary.npos) {
+ temporary.erase(0, last_slash + 1);
+ }
+ const char* basename = temporary.c_str();
+
+ printf(
+ "Usage: %s [-u] [-v] [-p] file\n\n"
+ "Pack or unpack relative relocations in a shared library.\n\n"
+ " -u, --unpack unpack previously packed relative relocations\n"
+ " -v, --verbose trace object file modifications (for debugging)\n"
+ " -p, --pad do not shrink relocations, but pad (for debugging)\n\n",
+ basename);
+
+ printf(
+ "Debug sections are not handled, so packing should not be used on\n"
+ "shared libraries compiled for debugging or otherwise unstripped.\n");
+}
+
+int main(int argc, char* argv[]) {
+ bool is_unpacking = false;
+ bool is_verbose = false;
+ bool is_padding = false;
+
+ static const option options[] = {
+ {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'},
+ {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}
+ };
+ bool has_options = true;
+ while (has_options) {
+ int c = getopt_long(argc, argv, "uvph", options, NULL);
+ switch (c) {
+ case 'u':
+ is_unpacking = true;
+ break;
+ case 'v':
+ is_verbose = true;
+ break;
+ case 'p':
+ is_padding = true;
+ break;
+ case 'h':
+ PrintUsage(argv[0]);
+ return 0;
+ case '?':
+ LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+ return 1;
+ case -1:
+ has_options = false;
+ break;
+ default:
+ NOTREACHED();
+ return 1;
+ }
+ }
+ if (optind != argc - 1) {
+ LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+ return 1;
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ LOG(WARNING) << "Elf Library is out of date!";
+ }
+
+ const char* file = argv[argc - 1];
+ ScopedFd fd(open(file, O_RDWR));
+ if (fd.get() == -1) {
+ LOG(ERROR) << file << ": " << strerror(errno);
+ return 1;
+ }
+
+ if (is_verbose)
+ relocation_packer::Logger::SetVerbose(1);
+
+ // We need to detect elf class in order to create
+ // correct implementation
+ uint8_t e_ident[EI_NIDENT];
+ if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) {
+ LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno);
+ return 1;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
+ LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
+ return 1;
+ }
+
+ bool status = false;
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ relocation_packer::ElfFile<ELF32_traits> elf_file(fd.get());
+ elf_file.SetPadding(is_padding);
+
+ if (is_unpacking) {
+ status = elf_file.UnpackRelocations();
+ } else {
+ status = elf_file.PackRelocations();
+ }
+ } else if (e_ident[EI_CLASS] == ELFCLASS64) {
+ relocation_packer::ElfFile<ELF64_traits> elf_file(fd.get());
+ elf_file.SetPadding(is_padding);
+
+ if (is_unpacking) {
+ status = elf_file.UnpackRelocations();
+ } else {
+ status = elf_file.PackRelocations();
+ }
+ } else {
+ LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS];
+ return 1;
+ }
+
+ if (!status) {
+ LOG(ERROR) << file << ": failed to pack/unpack file";
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc
new file mode 100644
index 0000000..8e30612
--- /dev/null
+++ b/tools/relocation_packer/src/packer.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+
+#include "debug.h"
+#include "delta_encoder.h"
+#include "elf_traits.h"
+#include "leb128.h"
+#include "sleb128.h"
+
+namespace relocation_packer {
+
+// Pack relocations into a group encoded packed representation.
+template <typename ELF>
+void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
+ std::vector<uint8_t>* packed) {
+ // Run-length encode.
+ std::vector<typename ELF::Addr> packed_words;
+ RelocationDeltaCodec<ELF> codec;
+ codec.Encode(relocations, &packed_words);
+
+ // If insufficient data do nothing.
+ if (packed_words.empty())
+ return;
+
+ Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
+ Leb128Encoder<typename ELF::Addr> leb128_encoder;
+
+ std::vector<uint8_t> leb128_packed;
+ std::vector<uint8_t> sleb128_packed;
+
+ leb128_encoder.EnqueueAll(packed_words);
+ leb128_encoder.GetEncoding(&leb128_packed);
+
+ sleb128_encoder.EnqueueAll(packed_words);
+ sleb128_encoder.GetEncoding(&sleb128_packed);
+
+ // TODO (simonb): Estimate savings on current android system image and consider using
+ // one encoder for all packed relocations to reduce complexity.
+ if (leb128_packed.size() <= sleb128_packed.size()) {
+ packed->push_back('A');
+ packed->push_back('P');
+ packed->push_back('U');
+ packed->push_back('2');
+ packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end());
+ } else {
+ packed->push_back('A');
+ packed->push_back('P');
+ packed->push_back('S');
+ packed->push_back('2');
+ packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end());
+ }
+}
+
+// Unpack relative relocations from a run-length encoded packed
+// representation.
+template <typename ELF>
+void RelocationPacker<ELF>::UnpackRelocations(
+ const std::vector<uint8_t>& packed,
+ std::vector<typename ELF::Rela>* relocations) {
+
+ std::vector<typename ELF::Addr> packed_words;
+ CHECK(packed.size() > 4 &&
+ packed[0] == 'A' &&
+ packed[1] == 'P' &&
+ (packed[2] == 'U' || packed[2] == 'S') &&
+ packed[3] == '2');
+
+ if (packed[2] == 'U') {
+ Leb128Decoder<typename ELF::Addr> decoder(packed, 4);
+ decoder.DequeueAll(&packed_words);
+ } else {
+ Sleb128Decoder<typename ELF::Addr> decoder(packed, 4);
+ decoder.DequeueAll(&packed_words);
+ }
+
+ RelocationDeltaCodec<ELF> codec;
+ codec.Decode(packed_words, relocations);
+}
+
+template class RelocationPacker<ELF32_traits>;
+template class RelocationPacker<ELF64_traits>;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h
new file mode 100644
index 0000000..8a57e62
--- /dev/null
+++ b/tools/relocation_packer/src/packer.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Pack relative relocations into a more compact form.
+//
+//
+// For relative relocations without addends (32 bit platforms)
+// -----------------------------------------------------------
+//
+// Applies two packing strategies. The first is run-length encoding, which
+// turns a large set of relative relocations into a much smaller set
+// of delta-count pairs, prefixed with a two-word header comprising the
+// count of pairs and the initial relocation offset. The second is LEB128
+// encoding, which compresses the result of run-length encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream of relocations without addends might look
+// something like:
+//
+// "APR1" pairs init_offset count1 delta1 count2 delta2 ...
+// 41505231 f2b003 b08ac716 e001 04 01 10 ...
+//
+//
+// For relative relocations with addends (64 bit platforms)
+// --------------------------------------------------------
+//
+// Applies two packing strategies. The first is delta encoding, which
+// turns a large set of relative relocations into a smaller set
+// of offset and addend delta pairs, prefixed with a header indicating the
+// count of pairs. The second is signed LEB128 encoding, which compacts
+// the result of delta encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream might look something like:
+//
+// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ...
+// 41505232 f2b018 04 28 08 9f01 ...
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf.h"
+
+namespace relocation_packer {
+
+// A RelocationPacker packs vectors of relocations into more
+// compact forms, and unpacks them to reproduce the pre-packed data.
+template <typename ELF>
+class RelocationPacker {
+ public:
+ // Pack relocations into a more compact form.
+ // |relocations| is a vector of relocation structs.
+ // |packed| is the vector of packed bytes into which relocations are packed.
+ static void PackRelocations(const std::vector<typename ELF::Rela>& relocations,
+ std::vector<uint8_t>* packed);
+
+ // Unpack relocations from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relocation structs.
+ static void UnpackRelocations(const std::vector<uint8_t>& packed,
+ std::vector<typename ELF::Rela>* relocations);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc
new file mode 100644
index 0000000..8dddd8b
--- /dev/null
+++ b/tools/relocation_packer/src/packer_unittest.cc
@@ -0,0 +1,292 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+
+template <typename ELF>
+static void AddRelocation(typename ELF::Addr addr,
+ typename ELF::Xword info,
+ typename ELF::Sxword addend,
+ std::vector<typename ELF::Rela>* relocations) {
+ typename ELF::Rela relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = info;
+ relocation.r_addend = addend;
+
+ relocations->push_back(relocation);
+}
+
+template <typename ELF>
+static bool CheckRelocation(typename ELF::Addr addr,
+ typename ELF::Xword info,
+ typename ELF::Sxword addend,
+ const typename ELF::Rela& relocation) {
+ return relocation.r_offset == addr &&
+ relocation.r_info == info &&
+ relocation.r_addend == addend;
+}
+
+namespace relocation_packer {
+
+template <typename ELF>
+static void DoPackNoAddend() {
+ std::vector<typename ELF::Rela> relocations;
+ std::vector<uint8_t> packed;
+ // Initial relocation.
+ AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
+ // Two more relocations, 4 byte deltas.
+ AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
+ AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
+ // Three more relocations, 8 byte deltas.
+ AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
+ AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
+ AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
+
+ RelocationPacker<ELF> packer;
+
+ packed.clear();
+ packer.PackRelocations(relocations, &packed);
+
+ ASSERT_EQ(18U, packed.size());
+ // Identifier.
+ size_t ndx = 0;
+ EXPECT_EQ('A', packed[ndx++]);
+ EXPECT_EQ('P', packed[ndx++]);
+ EXPECT_EQ('U', packed[ndx++]);
+ EXPECT_EQ('2', packed[ndx++]);
+ // relocation count
+ EXPECT_EQ(6, packed[ndx++]);
+ // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+ EXPECT_EQ(0xfc, packed[ndx++]);
+ EXPECT_EQ(0xff, packed[ndx++]);
+ EXPECT_EQ(0xb7, packed[ndx++]);
+ EXPECT_EQ(0x8e, packed[ndx++]);
+ EXPECT_EQ(0x0d, packed[ndx++]);
+ // first group
+ EXPECT_EQ(3, packed[ndx++]); // size
+ EXPECT_EQ(3, packed[ndx++]); // flags
+ EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
+ EXPECT_EQ(0x11, packed[ndx++]); // r_info
+ // second group
+ EXPECT_EQ(3, packed[ndx++]); // size
+ EXPECT_EQ(3, packed[ndx++]); // flags
+ EXPECT_EQ(8, packed[ndx++]); // r_offset_delta
+ EXPECT_EQ(0x11, packed[ndx++]); // r_info
+
+ EXPECT_EQ(ndx, packed.size());
+}
+
+TEST(Packer, PackNoAddend) {
+ DoPackNoAddend<ELF32_traits>();
+ DoPackNoAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoUnpackNoAddend() {
+ std::vector<typename ELF::Rela> relocations;
+ std::vector<uint8_t> packed;
+ packed.push_back('A');
+ packed.push_back('P');
+ packed.push_back('U');
+ packed.push_back('2');
+ // relocation count
+ packed.push_back(6);
+ // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+ packed.push_back(0xfc);
+ packed.push_back(0xff);
+ packed.push_back(0xb7);
+ packed.push_back(0x8e);
+ packed.push_back(0x0d);
+ // first group
+ packed.push_back(3); // size
+ packed.push_back(3); // flags
+ packed.push_back(4); // r_offset_delta
+ packed.push_back(0x11); // r_info
+ // second group
+ packed.push_back(3); // size
+ packed.push_back(3); // flags
+ packed.push_back(8); // r_offset_delta
+ packed.push_back(0x11); // r_info
+
+ RelocationPacker<ELF> packer;
+ packer.UnpackRelocations(packed, &relocations);
+
+ size_t ndx = 0;
+ EXPECT_EQ(6U, relocations.size());
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
+
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x11, 0, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x11, 0, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x11, 0, relocations[ndx++]));
+
+ EXPECT_EQ(ndx, relocations.size());
+}
+
+TEST(Packer, UnpackNoAddend) {
+ DoUnpackNoAddend<ELF32_traits>();
+ DoUnpackNoAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoPackWithAddend() {
+ std::vector<typename ELF::Rela> relocations;
+
+ // Initial relocation.
+ AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
+ // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+ AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
+ AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
+ // Three more relocations, 8 byte deltas, -24 byte addend deltas.
+ AddRelocation<ELF>(0xd1ce0010, 0x01, 10000, &relocations);
+ AddRelocation<ELF>(0xd1ce0018, 0x01, 9976, &relocations);
+ AddRelocation<ELF>(0xd1ce0020, 0x01, 9952, &relocations);
+
+ std::vector<uint8_t> packed;
+
+ RelocationPacker<ELF> packer;
+
+ packed.clear();
+ packer.PackRelocations(relocations, &packed);
+
+ EXPECT_EQ(26U, packed.size());
+ size_t ndx = 0;
+ // Identifier.
+ EXPECT_EQ('A', packed[ndx++]);
+ EXPECT_EQ('P', packed[ndx++]);
+ EXPECT_EQ('S', packed[ndx++]);
+ EXPECT_EQ('2', packed[ndx++]);
+ // Relocation count
+ EXPECT_EQ(6U, packed[ndx++]);
+ // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr)
+ EXPECT_EQ(0xfc, packed[ndx++]);
+ EXPECT_EQ(0xff, packed[ndx++]);
+ EXPECT_EQ(0xb7, packed[ndx++]);
+ EXPECT_EQ(0x8e, packed[ndx++]);
+ if (sizeof(typename ELF::Addr) == 8) {
+ // positive for uint64_t
+ EXPECT_EQ(0x0d, packed[ndx++]);
+ } else {
+ // negative for uint32_t
+ EXPECT_EQ(0x7d, packed[ndx++]);
+ }
+ // group 1
+ EXPECT_EQ(0x03, packed[ndx++]); // size
+ EXPECT_EQ(0x0b, packed[ndx++]); // flags
+ EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta
+ EXPECT_EQ(0x01, packed[ndx++]); // r_info
+ // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
+ EXPECT_EQ(0xa8, packed[ndx++]);
+ EXPECT_EQ(0xce, packed[ndx++]);
+ EXPECT_EQ(0x00, packed[ndx++]);
+ // group 1 - addend 2: -12 = 0x74
+ EXPECT_EQ(0x74, packed[ndx++]);
+ // group 1 - addend 3: +12 = 0x0c
+ EXPECT_EQ(0x0c, packed[ndx++]);
+
+ // group 2
+ EXPECT_EQ(0x03, packed[ndx++]); // size
+ EXPECT_EQ(0x0b, packed[ndx++]); // flags
+ EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta
+ EXPECT_EQ(0x01, packed[ndx++]); // r_info
+
+ // group 2 - addend 1: -24 = 0x68
+ EXPECT_EQ(0x68, packed[ndx++]);
+ // group 2 - addend 2: -24 = 0x68
+ EXPECT_EQ(0x68, packed[ndx++]);
+ // group 2 - addend 3: -24 = 0x68
+ EXPECT_EQ(0x68, packed[ndx++]);
+
+ EXPECT_EQ(ndx, packed.size());
+}
+
+TEST(Packer, PackWithAddend) {
+ DoPackWithAddend<ELF32_traits>();
+ DoPackWithAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoUnpackWithAddend() {
+ std::vector<uint8_t> packed;
+ // Identifier.
+ packed.push_back('A');
+ packed.push_back('P');
+ packed.push_back('S');
+ packed.push_back('2');
+ // Relocation count
+ packed.push_back(6U);
+ // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+ packed.push_back(0xfc);
+ packed.push_back(0xff);
+ packed.push_back(0xb7);
+ packed.push_back(0x8e);
+ if (sizeof(typename ELF::Addr) == 8) {
+ // positive for uint64_t
+ packed.push_back(0x0d);
+ } else {
+ // negative for uint32_t
+ packed.push_back(0x7d);
+ }
+ // group 1
+ packed.push_back(0x03); // size
+ packed.push_back(0x0b); // flags
+ packed.push_back(0x04); // r_offset_delta
+ packed.push_back(0x01); // r_info
+ // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
+ packed.push_back(0xa8);
+ packed.push_back(0xce);
+ packed.push_back(0x00);
+ // group 1 - addend 2: -12 = 0x74
+ packed.push_back(0x74);
+ // group 1 - addend 3: +12 = 0x0c
+ packed.push_back(0x0c);
+
+ // group 2
+ packed.push_back(0x03); // size
+ packed.push_back(0x0b); // flags
+ packed.push_back(0x08); // r_offset_delta
+ packed.push_back(0x01); // r_info
+
+ // group 2 - addend 1: -24 = 0x68
+ packed.push_back(0x68);
+ // group 2 - addend 2: -24 = 0x68
+ packed.push_back(0x68);
+ // group 2 - addend 3: -24 = 0x68
+ packed.push_back(0x68);
+
+ std::vector<typename ELF::Rela> relocations;
+
+ RelocationPacker<ELF> packer;
+
+ relocations.clear();
+ packer.UnpackRelocations(packed, &relocations);
+
+ EXPECT_EQ(6U, relocations.size());
+ size_t ndx = 0;
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
+ // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
+ // Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
+ EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x01, 9952, relocations[ndx++]));
+
+ EXPECT_EQ(ndx, relocations.size());
+}
+
+TEST(Packer, UnpackWithAddend) {
+ DoUnpackWithAddend<ELF32_traits>();
+ DoUnpackWithAddend<ELF64_traits>();
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h
new file mode 100644
index 0000000..f3a80e6
--- /dev/null
+++ b/tools/relocation_packer/src/run_length_encoder.h
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Run-length encode and decode relative relocations.
+//
+// Relative relocations are the bulk of dynamic relocations (the
+// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
+// standard representation of them is wasteful. .rel.dyn contains
+// relocations without addends, .rela.dyn relocations with addends.
+//
+// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes
+// on 64 bit plaforms, split into offset and info fields. Offsets strictly
+// increase, and each is commonly a few bytes different from its predecessor.
+// There are long runs where the difference does not change. The info field
+// is constant. Example, from 'readelf -x4 libchrome.<version>.so' 32 bit:
+//
+// offset info offset info
+// 808fef01 17000000 848fef01 17000000 ................
+// 888fef01 17000000 8c8fef01 17000000 ................
+// 908fef01 17000000 948fef01 17000000 ................
+//
+// Run length encoding packs this data more efficiently, by representing it
+// as a delta and a count of entries each differing from its predecessor
+// by this delta. The above can be represented as a start address followed
+// by an encoded count of 6 and offset difference of 4:
+//
+// start count diff
+// 01ef8f80 00000006 00000004
+//
+// Because relative relocation offsets strictly increase, the complete
+// set of relative relocations in libchrome.<version>.so can be
+// represented by a single start address followed by one or more difference
+// and count encoded word pairs:
+//
+// start run1 count run1 diff run2 count run2 diff
+// 01ef8f80 00000006 00000004 00000010 00000008 ...
+//
+// Decoding regenerates relative relocations beginning at address
+// 'start' and for each encoded run, incrementing the address by 'difference'
+// for 'count' iterations and emitting a new relative relocation.
+//
+// Once encoded, data is prefixed by a single word count of packed delta and
+// count pairs. A final run-length encoded relative relocations vector
+// might therefore look something like:
+//
+// pairs start run 1 run 2 ... run 15
+// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ...
+// Interpreted as:
+// pairs=15 start=.. count=6,delta=4 count=16,delta=8
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
+
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationRunLengthCodec packs vectors of relative relocations
+// into more compact forms, and unpacks them to reproduce the pre-packed data.
+class RelocationRunLengthCodec {
+ public:
+ // Encode relative relocations into a more compact form.
+ // |relocations| is a vector of relative relocation structs.
+ // |packed| is the vector of packed words into which relocations are packed.
+ static void Encode(const std::vector<ELF::Rel>& relocations,
+ std::vector<ELF::Xword>* packed);
+
+ // Decode relative relocations from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relative relocation structs.
+ static void Decode(const std::vector<ELF::Xword>& packed,
+ std::vector<ELF::Rel>* relocations);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc
new file mode 100644
index 0000000..12c21e3
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace {
+
+template <typename T>
+class uint_traits {};
+
+template <>
+class uint_traits<uint64_t> {
+ public:
+ typedef int64_t int_t;
+};
+
+template <>
+class uint_traits<uint32_t> {
+ public:
+ typedef int32_t int_t;
+};
+
+}
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+template <typename uint_t>
+Sleb128Encoder<uint_t>::Sleb128Encoder() { }
+
+template <typename uint_t>
+Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
+
+// Add a single value to the encoding. Values are encoded with variable
+// length. The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last. The
+// value is sign extended up to a multiple of 7 bits (ensuring that the
+// most significant bit is zero for a positive number and one for a
+// negative number).
+template <typename uint_t>
+void Sleb128Encoder<uint_t>::Enqueue(uint_t value) {
+ typedef typename uint_traits<uint_t>::int_t int_t;
+ static const size_t size = CHAR_BIT * sizeof(value);
+
+ bool more = true;
+ const bool negative = static_cast<int_t>(value) < 0;
+
+ while (more) {
+ uint8_t byte = value & 127;
+ value >>= 7;
+
+ // Sign extend if encoding a -ve value.
+ if (negative)
+ value |= -(static_cast<uint_t>(1) << (size - 7));
+
+ // The sign bit of byte is second high order bit.
+ const bool sign_bit = byte & 64;
+ if ((value == 0 && !sign_bit) || (value == static_cast<uint_t>(-1) && sign_bit))
+ more = false;
+ else
+ byte |= 128;
+ encoding_.push_back(byte);
+ }
+}
+
+// Add a vector of values to the encoding.
+template <typename uint_t>
+void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
+ for (size_t i = 0; i < values.size(); ++i) {
+ Enqueue(values[i]);
+ }
+}
+
+// Create a new decoder for the given encoded stream.
+template <typename uint_t>
+Sleb128Decoder<uint_t>::Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
+ encoding_ = encoding;
+ cursor_ = start_with;
+}
+
+// Empty destructor to silence chromium-style.
+template <typename uint_t>
+Sleb128Decoder<uint_t>::~Sleb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding. Consume bytes
+// until one without its most significant bit is found, and re-form the
+// value from the 7 bit fields of the bytes consumed.
+template <typename uint_t>
+uint_t Sleb128Decoder<uint_t>::Dequeue() {
+ uint_t value = 0;
+ static const size_t size = CHAR_BIT * sizeof(value);
+
+ size_t shift = 0;
+ uint8_t byte;
+
+ // Loop until we reach a byte with its high order bit clear.
+ do {
+ byte = encoding_[cursor_++];
+ value |= (static_cast<uint_t>(byte & 127) << shift);
+ shift += 7;
+ } while (byte & 128);
+
+ // The sign bit is second high order bit of the final byte decoded.
+ // Sign extend if value is -ve and we did not shift all of it.
+ if (shift < size && (byte & 64))
+ value |= -(static_cast<uint_t>(1) << shift);
+
+ return static_cast<uint_t>(value);
+}
+
+// Decode and retrieve all remaining values from the encoding.
+template <typename uint_t>
+void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
+ while (cursor_ < encoding_.size()) {
+ values->push_back(Dequeue());
+ }
+}
+
+template class Sleb128Encoder<uint32_t>;
+template class Sleb128Encoder<uint64_t>;
+template class Sleb128Decoder<uint32_t>;
+template class Sleb128Decoder<uint64_t>;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h
new file mode 100644
index 0000000..fa0a246
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// SLEB128 encoder and decoder for packed relative relocations.
+//
+// Delta encoded relative relocations consist of a large number
+// of pairs signed integer values, many with small values. Encoding these
+// as signed LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a signed LEB128 byte stream.
+template<typename int_t>
+class Sleb128Encoder {
+ public:
+ // Explicit (but empty) constructor and destructor, for chromium-style.
+ Sleb128Encoder();
+ ~Sleb128Encoder();
+
+ // Add a value to the encoding stream.
+ // |value| is the signed int to add.
+ void Enqueue(int_t value);
+
+ // Add a vector of values to the encoding stream.
+ // |values| is the vector of signed ints to add.
+ void EnqueueAll(const std::vector<int_t>& values);
+
+ // Retrieve the encoded representation of the values.
+ // |encoding| is the returned vector of encoded data.
+ void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+ // Growable vector holding the encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+template <typename int_t>
+class Sleb128Decoder {
+ public:
+ // Create a new decoder for the given encoded stream.
+ // |encoding| is the vector of encoded data.
+ explicit Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
+
+ // Explicit (but empty) destructor, for chromium-style.
+ ~Sleb128Decoder();
+
+ // Retrieve the next value from the encoded stream.
+ int_t Dequeue();
+
+ // Retrieve all remaining values from the encoded stream.
+ // |values| is the vector of decoded data.
+ void DequeueAll(std::vector<int_t>* values);
+
+ private:
+ // Encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+
+ // Cursor indicating the current stream retrieval point.
+ size_t cursor_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc
new file mode 100644
index 0000000..49a553c
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <vector>
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Sleb128, Encoder64) {
+ std::vector<uint64_t> values;
+ values.push_back(624485U);
+ values.push_back(0U);
+ values.push_back(1U);
+ values.push_back(63U);
+ values.push_back(64U);
+ values.push_back(static_cast<uint64_t>(-1));
+ values.push_back(static_cast<uint64_t>(-624485));
+
+ Sleb128Encoder<uint64_t> encoder;
+ encoder.EnqueueAll(values);
+
+ encoder.Enqueue(2147483647U);
+ encoder.Enqueue(static_cast<uint64_t>(-2147483648));
+ encoder.Enqueue(9223372036854775807ULL);
+ encoder.Enqueue(static_cast<uint64_t>(-9223372036854775807LL - 1));
+
+ std::vector<uint8_t> encoding;
+ encoder.GetEncoding(&encoding);
+
+ EXPECT_EQ(42u, encoding.size());
+ // 624485
+ EXPECT_EQ(0xe5, encoding[0]);
+ EXPECT_EQ(0x8e, encoding[1]);
+ EXPECT_EQ(0x26, encoding[2]);
+ // 0
+ EXPECT_EQ(0x00, encoding[3]);
+ // 1
+ EXPECT_EQ(0x01, encoding[4]);
+ // 63
+ EXPECT_EQ(0x3f, encoding[5]);
+ // 64
+ EXPECT_EQ(0xc0, encoding[6]);
+ EXPECT_EQ(0x00, encoding[7]);
+ // -1
+ EXPECT_EQ(0x7f, encoding[8]);
+ // -624485
+ EXPECT_EQ(0x9b, encoding[9]);
+ EXPECT_EQ(0xf1, encoding[10]);
+ EXPECT_EQ(0x59, encoding[11]);
+ // 2147483647
+ EXPECT_EQ(0xff, encoding[12]);
+ EXPECT_EQ(0xff, encoding[13]);
+ EXPECT_EQ(0xff, encoding[14]);
+ EXPECT_EQ(0xff, encoding[15]);
+ EXPECT_EQ(0x07, encoding[16]);
+ // -2147483648
+ EXPECT_EQ(0x80, encoding[17]);
+ EXPECT_EQ(0x80, encoding[18]);
+ EXPECT_EQ(0x80, encoding[19]);
+ EXPECT_EQ(0x80, encoding[20]);
+ EXPECT_EQ(0x78, encoding[21]);
+ // 9223372036854775807
+ EXPECT_EQ(0xff, encoding[22]);
+ EXPECT_EQ(0xff, encoding[23]);
+ EXPECT_EQ(0xff, encoding[24]);
+ EXPECT_EQ(0xff, encoding[25]);
+ EXPECT_EQ(0xff, encoding[26]);
+ EXPECT_EQ(0xff, encoding[27]);
+ EXPECT_EQ(0xff, encoding[28]);
+ EXPECT_EQ(0xff, encoding[29]);
+ EXPECT_EQ(0xff, encoding[30]);
+ EXPECT_EQ(0x00, encoding[31]);
+ // -9223372036854775808
+ EXPECT_EQ(0x80, encoding[32]);
+ EXPECT_EQ(0x80, encoding[33]);
+ EXPECT_EQ(0x80, encoding[34]);
+ EXPECT_EQ(0x80, encoding[35]);
+ EXPECT_EQ(0x80, encoding[36]);
+ EXPECT_EQ(0x80, encoding[37]);
+ EXPECT_EQ(0x80, encoding[38]);
+ EXPECT_EQ(0x80, encoding[39]);
+ EXPECT_EQ(0x80, encoding[40]);
+ EXPECT_EQ(0x7f, encoding[41]);
+}
+
+TEST(Sleb128, Decoder) {
+ 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);
+ // 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);
+
+ Sleb128Decoder<uint64_t> decoder(encoding, 0);
+
+ EXPECT_EQ(624485U, decoder.Dequeue());
+
+ std::vector<uint64_t> dequeued;
+ decoder.DequeueAll(&dequeued);
+
+ EXPECT_EQ(10U, dequeued.size());
+ EXPECT_EQ(0U, dequeued[0]);
+ EXPECT_EQ(1U, dequeued[1]);
+ EXPECT_EQ(63U, dequeued[2]);
+ EXPECT_EQ(64U, dequeued[3]);
+ EXPECT_EQ(static_cast<uint64_t>(-1), dequeued[4]);
+ EXPECT_EQ(static_cast<uint64_t>(-624485), dequeued[5]);
+ EXPECT_EQ(2147483647U, dequeued[6]);
+ EXPECT_EQ(static_cast<uint64_t>(-2147483648), dequeued[7]);
+ EXPECT_EQ(9223372036854775807ULL, dequeued[8]);
+ EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), dequeued[9]);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
new file mode 100644
index 0000000..5e1fa74
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
@@ -0,0 +1,1014 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test data for packing/unpacking. When compiled, creates a run of
+// relative relocations.
+//
+// See generate_elf_file_unittest_relocs.sh for instructions on how to build
+// unit test data from this source file.
+
+const int i = 0;
+
+// Generator:
+// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i'
+const void* pointer_0 = &i;
+const void* pointer_1 = &i;
+const void* pointer_2 = &i;
+const void* pointer_3 = &i;
+const void* pointer_4 = &i;
+const void* pointer_5 = &i;
+const void* pointer_6 = &i;
+const void* pointer_7 = &i;
+const void* pointer_8 = &i;
+const void* pointer_9 = &i;
+const void* pointer_10 = &i;
+const void* pointer_11 = &i;
+const void* pointer_12 = &i;
+const void* pointer_13 = &i;
+const void* pointer_14 = &i;
+const void* pointer_15 = &i;
+const void* pointer_16 = &i;
+const void* pointer_17 = &i;
+const void* pointer_18 = &i;
+const void* pointer_19 = &i;
+const void* pointer_20 = &i;
+const void* pointer_21 = &i;
+const void* pointer_22 = &i;
+const void* pointer_23 = &i;
+const void* pointer_24 = &i;
+const void* pointer_25 = &i;
+const void* pointer_26 = &i;
+const void* pointer_27 = &i;
+const void* pointer_28 = &i;
+const void* pointer_29 = &i;
+const void* pointer_30 = &i;
+const void* pointer_31 = &i;
+const void* pointer_32 = &i;
+const void* pointer_33 = &i;
+const void* pointer_34 = &i;
+const void* pointer_35 = &i;
+const void* pointer_36 = &i;
+const void* pointer_37 = &i;
+const void* pointer_38 = &i;
+const void* pointer_39 = &i;
+const void* pointer_40 = &i;
+const void* pointer_41 = &i;
+const void* pointer_42 = &i;
+const void* pointer_43 = &i;
+const void* pointer_44 = &i;
+const void* pointer_45 = &i;
+const void* pointer_46 = &i;
+const void* pointer_47 = &i;
+const void* pointer_48 = &i;
+const void* pointer_49 = &i;
+const void* pointer_50 = &i;
+const void* pointer_51 = &i;
+const void* pointer_52 = &i;
+const void* pointer_53 = &i;
+const void* pointer_54 = &i;
+const void* pointer_55 = &i;
+const void* pointer_56 = &i;
+const void* pointer_57 = &i;
+const void* pointer_58 = &i;
+const void* pointer_59 = &i;
+const void* pointer_60 = &i;
+const void* pointer_61 = &i;
+const void* pointer_62 = &i;
+const void* pointer_63 = &i;
+const void* pointer_64 = &i;
+const void* pointer_65 = &i;
+const void* pointer_66 = &i;
+const void* pointer_67 = &i;
+const void* pointer_68 = &i;
+const void* pointer_69 = &i;
+const void* pointer_70 = &i;
+const void* pointer_71 = &i;
+const void* pointer_72 = &i;
+const void* pointer_73 = &i;
+const void* pointer_74 = &i;
+const void* pointer_75 = &i;
+const void* pointer_76 = &i;
+const void* pointer_77 = &i;
+const void* pointer_78 = &i;
+const void* pointer_79 = &i;
+const void* pointer_80 = &i;
+const void* pointer_81 = &i;
+const void* pointer_82 = &i;
+const void* pointer_83 = &i;
+const void* pointer_84 = &i;
+const void* pointer_85 = &i;
+const void* pointer_86 = &i;
+const void* pointer_87 = &i;
+const void* pointer_88 = &i;
+const void* pointer_89 = &i;
+const void* pointer_90 = &i;
+const void* pointer_91 = &i;
+const void* pointer_92 = &i;
+const void* pointer_93 = &i;
+const void* pointer_94 = &i;
+const void* pointer_95 = &i;
+const void* pointer_96 = &i;
+const void* pointer_97 = &i;
+const void* pointer_98 = &i;
+const void* pointer_99 = &i;
+const void* pointer_100 = &i;
+const void* pointer_101 = &i;
+const void* pointer_102 = &i;
+const void* pointer_103 = &i;
+const void* pointer_104 = &i;
+const void* pointer_105 = &i;
+const void* pointer_106 = &i;
+const void* pointer_107 = &i;
+const void* pointer_108 = &i;
+const void* pointer_109 = &i;
+const void* pointer_110 = &i;
+const void* pointer_111 = &i;
+const void* pointer_112 = &i;
+const void* pointer_113 = &i;
+const void* pointer_114 = &i;
+const void* pointer_115 = &i;
+const void* pointer_116 = &i;
+const void* pointer_117 = &i;
+const void* pointer_118 = &i;
+const void* pointer_119 = &i;
+const void* pointer_120 = &i;
+const void* pointer_121 = &i;
+const void* pointer_122 = &i;
+const void* pointer_123 = &i;
+const void* pointer_124 = &i;
+const void* pointer_125 = &i;
+const void* pointer_126 = &i;
+const void* pointer_127 = &i;
+const void* pointer_128 = &i;
+const void* pointer_129 = &i;
+const void* pointer_130 = &i;
+const void* pointer_131 = &i;
+const void* pointer_132 = &i;
+const void* pointer_133 = &i;
+const void* pointer_134 = &i;
+const void* pointer_135 = &i;
+const void* pointer_136 = &i;
+const void* pointer_137 = &i;
+const void* pointer_138 = &i;
+const void* pointer_139 = &i;
+const void* pointer_140 = &i;
+const void* pointer_141 = &i;
+const void* pointer_142 = &i;
+const void* pointer_143 = &i;
+const void* pointer_144 = &i;
+const void* pointer_145 = &i;
+const void* pointer_146 = &i;
+const void* pointer_147 = &i;
+const void* pointer_148 = &i;
+const void* pointer_149 = &i;
+const void* pointer_150 = &i;
+const void* pointer_151 = &i;
+const void* pointer_152 = &i;
+const void* pointer_153 = &i;
+const void* pointer_154 = &i;
+const void* pointer_155 = &i;
+const void* pointer_156 = &i;
+const void* pointer_157 = &i;
+const void* pointer_158 = &i;
+const void* pointer_159 = &i;
+const void* pointer_160 = &i;
+const void* pointer_161 = &i;
+const void* pointer_162 = &i;
+const void* pointer_163 = &i;
+const void* pointer_164 = &i;
+const void* pointer_165 = &i;
+const void* pointer_166 = &i;
+const void* pointer_167 = &i;
+const void* pointer_168 = &i;
+const void* pointer_169 = &i;
+const void* pointer_170 = &i;
+const void* pointer_171 = &i;
+const void* pointer_172 = &i;
+const void* pointer_173 = &i;
+const void* pointer_174 = &i;
+const void* pointer_175 = &i;
+const void* pointer_176 = &i;
+const void* pointer_177 = &i;
+const void* pointer_178 = &i;
+const void* pointer_179 = &i;
+const void* pointer_180 = &i;
+const void* pointer_181 = &i;
+const void* pointer_182 = &i;
+const void* pointer_183 = &i;
+const void* pointer_184 = &i;
+const void* pointer_185 = &i;
+const void* pointer_186 = &i;
+const void* pointer_187 = &i;
+const void* pointer_188 = &i;
+const void* pointer_189 = &i;
+const void* pointer_190 = &i;
+const void* pointer_191 = &i;
+const void* pointer_192 = &i;
+const void* pointer_193 = &i;
+const void* pointer_194 = &i;
+const void* pointer_195 = &i;
+const void* pointer_196 = &i;
+const void* pointer_197 = &i;
+const void* pointer_198 = &i;
+const void* pointer_199 = &i;
+const void* pointer_200 = &i;
+const void* pointer_201 = &i;
+const void* pointer_202 = &i;
+const void* pointer_203 = &i;
+const void* pointer_204 = &i;
+const void* pointer_205 = &i;
+const void* pointer_206 = &i;
+const void* pointer_207 = &i;
+const void* pointer_208 = &i;
+const void* pointer_209 = &i;
+const void* pointer_210 = &i;
+const void* pointer_211 = &i;
+const void* pointer_212 = &i;
+const void* pointer_213 = &i;
+const void* pointer_214 = &i;
+const void* pointer_215 = &i;
+const void* pointer_216 = &i;
+const void* pointer_217 = &i;
+const void* pointer_218 = &i;
+const void* pointer_219 = &i;
+const void* pointer_220 = &i;
+const void* pointer_221 = &i;
+const void* pointer_222 = &i;
+const void* pointer_223 = &i;
+const void* pointer_224 = &i;
+const void* pointer_225 = &i;
+const void* pointer_226 = &i;
+const void* pointer_227 = &i;
+const void* pointer_228 = &i;
+const void* pointer_229 = &i;
+const void* pointer_230 = &i;
+const void* pointer_231 = &i;
+const void* pointer_232 = &i;
+const void* pointer_233 = &i;
+const void* pointer_234 = &i;
+const void* pointer_235 = &i;
+const void* pointer_236 = &i;
+const void* pointer_237 = &i;
+const void* pointer_238 = &i;
+const void* pointer_239 = &i;
+const void* pointer_240 = &i;
+const void* pointer_241 = &i;
+const void* pointer_242 = &i;
+const void* pointer_243 = &i;
+const void* pointer_244 = &i;
+const void* pointer_245 = &i;
+const void* pointer_246 = &i;
+const void* pointer_247 = &i;
+const void* pointer_248 = &i;
+const void* pointer_249 = &i;
+const void* pointer_250 = &i;
+const void* pointer_251 = &i;
+const void* pointer_252 = &i;
+const void* pointer_253 = &i;
+const void* pointer_254 = &i;
+const void* pointer_255 = &i;
+const void* pointer_256 = &i;
+const void* pointer_257 = &i;
+const void* pointer_258 = &i;
+const void* pointer_259 = &i;
+const void* pointer_260 = &i;
+const void* pointer_261 = &i;
+const void* pointer_262 = &i;
+const void* pointer_263 = &i;
+const void* pointer_264 = &i;
+const void* pointer_265 = &i;
+const void* pointer_266 = &i;
+const void* pointer_267 = &i;
+const void* pointer_268 = &i;
+const void* pointer_269 = &i;
+const void* pointer_270 = &i;
+const void* pointer_271 = &i;
+const void* pointer_272 = &i;
+const void* pointer_273 = &i;
+const void* pointer_274 = &i;
+const void* pointer_275 = &i;
+const void* pointer_276 = &i;
+const void* pointer_277 = &i;
+const void* pointer_278 = &i;
+const void* pointer_279 = &i;
+const void* pointer_280 = &i;
+const void* pointer_281 = &i;
+const void* pointer_282 = &i;
+const void* pointer_283 = &i;
+const void* pointer_284 = &i;
+const void* pointer_285 = &i;
+const void* pointer_286 = &i;
+const void* pointer_287 = &i;
+const void* pointer_288 = &i;
+const void* pointer_289 = &i;
+const void* pointer_290 = &i;
+const void* pointer_291 = &i;
+const void* pointer_292 = &i;
+const void* pointer_293 = &i;
+const void* pointer_294 = &i;
+const void* pointer_295 = &i;
+const void* pointer_296 = &i;
+const void* pointer_297 = &i;
+const void* pointer_298 = &i;
+const void* pointer_299 = &i;
+const void* pointer_300 = &i;
+const void* pointer_301 = &i;
+const void* pointer_302 = &i;
+const void* pointer_303 = &i;
+const void* pointer_304 = &i;
+const void* pointer_305 = &i;
+const void* pointer_306 = &i;
+const void* pointer_307 = &i;
+const void* pointer_308 = &i;
+const void* pointer_309 = &i;
+const void* pointer_310 = &i;
+const void* pointer_311 = &i;
+const void* pointer_312 = &i;
+const void* pointer_313 = &i;
+const void* pointer_314 = &i;
+const void* pointer_315 = &i;
+const void* pointer_316 = &i;
+const void* pointer_317 = &i;
+const void* pointer_318 = &i;
+const void* pointer_319 = &i;
+const void* pointer_320 = &i;
+const void* pointer_321 = &i;
+const void* pointer_322 = &i;
+const void* pointer_323 = &i;
+const void* pointer_324 = &i;
+const void* pointer_325 = &i;
+const void* pointer_326 = &i;
+const void* pointer_327 = &i;
+const void* pointer_328 = &i;
+const void* pointer_329 = &i;
+const void* pointer_330 = &i;
+const void* pointer_331 = &i;
+const void* pointer_332 = &i;
+const void* pointer_333 = &i;
+const void* pointer_334 = &i;
+const void* pointer_335 = &i;
+const void* pointer_336 = &i;
+const void* pointer_337 = &i;
+const void* pointer_338 = &i;
+const void* pointer_339 = &i;
+const void* pointer_340 = &i;
+const void* pointer_341 = &i;
+const void* pointer_342 = &i;
+const void* pointer_343 = &i;
+const void* pointer_344 = &i;
+const void* pointer_345 = &i;
+const void* pointer_346 = &i;
+const void* pointer_347 = &i;
+const void* pointer_348 = &i;
+const void* pointer_349 = &i;
+const void* pointer_350 = &i;
+const void* pointer_351 = &i;
+const void* pointer_352 = &i;
+const void* pointer_353 = &i;
+const void* pointer_354 = &i;
+const void* pointer_355 = &i;
+const void* pointer_356 = &i;
+const void* pointer_357 = &i;
+const void* pointer_358 = &i;
+const void* pointer_359 = &i;
+const void* pointer_360 = &i;
+const void* pointer_361 = &i;
+const void* pointer_362 = &i;
+const void* pointer_363 = &i;
+const void* pointer_364 = &i;
+const void* pointer_365 = &i;
+const void* pointer_366 = &i;
+const void* pointer_367 = &i;
+const void* pointer_368 = &i;
+const void* pointer_369 = &i;
+const void* pointer_370 = &i;
+const void* pointer_371 = &i;
+const void* pointer_372 = &i;
+const void* pointer_373 = &i;
+const void* pointer_374 = &i;
+const void* pointer_375 = &i;
+const void* pointer_376 = &i;
+const void* pointer_377 = &i;
+const void* pointer_378 = &i;
+const void* pointer_379 = &i;
+const void* pointer_380 = &i;
+const void* pointer_381 = &i;
+const void* pointer_382 = &i;
+const void* pointer_383 = &i;
+const void* pointer_384 = &i;
+const void* pointer_385 = &i;
+const void* pointer_386 = &i;
+const void* pointer_387 = &i;
+const void* pointer_388 = &i;
+const void* pointer_389 = &i;
+const void* pointer_390 = &i;
+const void* pointer_391 = &i;
+const void* pointer_392 = &i;
+const void* pointer_393 = &i;
+const void* pointer_394 = &i;
+const void* pointer_395 = &i;
+const void* pointer_396 = &i;
+const void* pointer_397 = &i;
+const void* pointer_398 = &i;
+const void* pointer_399 = &i;
+const void* pointer_400 = &i;
+const void* pointer_401 = &i;
+const void* pointer_402 = &i;
+const void* pointer_403 = &i;
+const void* pointer_404 = &i;
+const void* pointer_405 = &i;
+const void* pointer_406 = &i;
+const void* pointer_407 = &i;
+const void* pointer_408 = &i;
+const void* pointer_409 = &i;
+const void* pointer_410 = &i;
+const void* pointer_411 = &i;
+const void* pointer_412 = &i;
+const void* pointer_413 = &i;
+const void* pointer_414 = &i;
+const void* pointer_415 = &i;
+const void* pointer_416 = &i;
+const void* pointer_417 = &i;
+const void* pointer_418 = &i;
+const void* pointer_419 = &i;
+const void* pointer_420 = &i;
+const void* pointer_421 = &i;
+const void* pointer_422 = &i;
+const void* pointer_423 = &i;
+const void* pointer_424 = &i;
+const void* pointer_425 = &i;
+const void* pointer_426 = &i;
+const void* pointer_427 = &i;
+const void* pointer_428 = &i;
+const void* pointer_429 = &i;
+const void* pointer_430 = &i;
+const void* pointer_431 = &i;
+const void* pointer_432 = &i;
+const void* pointer_433 = &i;
+const void* pointer_434 = &i;
+const void* pointer_435 = &i;
+const void* pointer_436 = &i;
+const void* pointer_437 = &i;
+const void* pointer_438 = &i;
+const void* pointer_439 = &i;
+const void* pointer_440 = &i;
+const void* pointer_441 = &i;
+const void* pointer_442 = &i;
+const void* pointer_443 = &i;
+const void* pointer_444 = &i;
+const void* pointer_445 = &i;
+const void* pointer_446 = &i;
+const void* pointer_447 = &i;
+const void* pointer_448 = &i;
+const void* pointer_449 = &i;
+const void* pointer_450 = &i;
+const void* pointer_451 = &i;
+const void* pointer_452 = &i;
+const void* pointer_453 = &i;
+const void* pointer_454 = &i;
+const void* pointer_455 = &i;
+const void* pointer_456 = &i;
+const void* pointer_457 = &i;
+const void* pointer_458 = &i;
+const void* pointer_459 = &i;
+const void* pointer_460 = &i;
+const void* pointer_461 = &i;
+const void* pointer_462 = &i;
+const void* pointer_463 = &i;
+const void* pointer_464 = &i;
+const void* pointer_465 = &i;
+const void* pointer_466 = &i;
+const void* pointer_467 = &i;
+const void* pointer_468 = &i;
+const void* pointer_469 = &i;
+const void* pointer_470 = &i;
+const void* pointer_471 = &i;
+const void* pointer_472 = &i;
+const void* pointer_473 = &i;
+const void* pointer_474 = &i;
+const void* pointer_475 = &i;
+const void* pointer_476 = &i;
+const void* pointer_477 = &i;
+const void* pointer_478 = &i;
+const void* pointer_479 = &i;
+const void* pointer_480 = &i;
+const void* pointer_481 = &i;
+const void* pointer_482 = &i;
+const void* pointer_483 = &i;
+const void* pointer_484 = &i;
+const void* pointer_485 = &i;
+const void* pointer_486 = &i;
+const void* pointer_487 = &i;
+const void* pointer_488 = &i;
+const void* pointer_489 = &i;
+const void* pointer_490 = &i;
+const void* pointer_491 = &i;
+const void* pointer_492 = &i;
+const void* pointer_493 = &i;
+const void* pointer_494 = &i;
+const void* pointer_495 = &i;
+const void* pointer_496 = &i;
+const void* pointer_497 = &i;
+const void* pointer_498 = &i;
+const void* pointer_499 = &i;
+const void* pointer_500 = &i;
+const void* pointer_501 = &i;
+const void* pointer_502 = &i;
+const void* pointer_503 = &i;
+const void* pointer_504 = &i;
+const void* pointer_505 = &i;
+const void* pointer_506 = &i;
+const void* pointer_507 = &i;
+const void* pointer_508 = &i;
+const void* pointer_509 = &i;
+const void* pointer_510 = &i;
+const void* pointer_511 = &i;
+const void* pointer_512 = &i;
+const void* pointer_513 = &i;
+const void* pointer_514 = &i;
+const void* pointer_515 = &i;
+const void* pointer_516 = &i;
+const void* pointer_517 = &i;
+const void* pointer_518 = &i;
+const void* pointer_519 = &i;
+const void* pointer_520 = &i;
+const void* pointer_521 = &i;
+const void* pointer_522 = &i;
+const void* pointer_523 = &i;
+const void* pointer_524 = &i;
+const void* pointer_525 = &i;
+const void* pointer_526 = &i;
+const void* pointer_527 = &i;
+const void* pointer_528 = &i;
+const void* pointer_529 = &i;
+const void* pointer_530 = &i;
+const void* pointer_531 = &i;
+const void* pointer_532 = &i;
+const void* pointer_533 = &i;
+const void* pointer_534 = &i;
+const void* pointer_535 = &i;
+const void* pointer_536 = &i;
+const void* pointer_537 = &i;
+const void* pointer_538 = &i;
+const void* pointer_539 = &i;
+const void* pointer_540 = &i;
+const void* pointer_541 = &i;
+const void* pointer_542 = &i;
+const void* pointer_543 = &i;
+const void* pointer_544 = &i;
+const void* pointer_545 = &i;
+const void* pointer_546 = &i;
+const void* pointer_547 = &i;
+const void* pointer_548 = &i;
+const void* pointer_549 = &i;
+const void* pointer_550 = &i;
+const void* pointer_551 = &i;
+const void* pointer_552 = &i;
+const void* pointer_553 = &i;
+const void* pointer_554 = &i;
+const void* pointer_555 = &i;
+const void* pointer_556 = &i;
+const void* pointer_557 = &i;
+const void* pointer_558 = &i;
+const void* pointer_559 = &i;
+const void* pointer_560 = &i;
+const void* pointer_561 = &i;
+const void* pointer_562 = &i;
+const void* pointer_563 = &i;
+const void* pointer_564 = &i;
+const void* pointer_565 = &i;
+const void* pointer_566 = &i;
+const void* pointer_567 = &i;
+const void* pointer_568 = &i;
+const void* pointer_569 = &i;
+const void* pointer_570 = &i;
+const void* pointer_571 = &i;
+const void* pointer_572 = &i;
+const void* pointer_573 = &i;
+const void* pointer_574 = &i;
+const void* pointer_575 = &i;
+const void* pointer_576 = &i;
+const void* pointer_577 = &i;
+const void* pointer_578 = &i;
+const void* pointer_579 = &i;
+const void* pointer_580 = &i;
+const void* pointer_581 = &i;
+const void* pointer_582 = &i;
+const void* pointer_583 = &i;
+const void* pointer_584 = &i;
+const void* pointer_585 = &i;
+const void* pointer_586 = &i;
+const void* pointer_587 = &i;
+const void* pointer_588 = &i;
+const void* pointer_589 = &i;
+const void* pointer_590 = &i;
+const void* pointer_591 = &i;
+const void* pointer_592 = &i;
+const void* pointer_593 = &i;
+const void* pointer_594 = &i;
+const void* pointer_595 = &i;
+const void* pointer_596 = &i;
+const void* pointer_597 = &i;
+const void* pointer_598 = &i;
+const void* pointer_599 = &i;
+const void* pointer_600 = &i;
+const void* pointer_601 = &i;
+const void* pointer_602 = &i;
+const void* pointer_603 = &i;
+const void* pointer_604 = &i;
+const void* pointer_605 = &i;
+const void* pointer_606 = &i;
+const void* pointer_607 = &i;
+const void* pointer_608 = &i;
+const void* pointer_609 = &i;
+const void* pointer_610 = &i;
+const void* pointer_611 = &i;
+const void* pointer_612 = &i;
+const void* pointer_613 = &i;
+const void* pointer_614 = &i;
+const void* pointer_615 = &i;
+const void* pointer_616 = &i;
+const void* pointer_617 = &i;
+const void* pointer_618 = &i;
+const void* pointer_619 = &i;
+const void* pointer_620 = &i;
+const void* pointer_621 = &i;
+const void* pointer_622 = &i;
+const void* pointer_623 = &i;
+const void* pointer_624 = &i;
+const void* pointer_625 = &i;
+const void* pointer_626 = &i;
+const void* pointer_627 = &i;
+const void* pointer_628 = &i;
+const void* pointer_629 = &i;
+const void* pointer_630 = &i;
+const void* pointer_631 = &i;
+const void* pointer_632 = &i;
+const void* pointer_633 = &i;
+const void* pointer_634 = &i;
+const void* pointer_635 = &i;
+const void* pointer_636 = &i;
+const void* pointer_637 = &i;
+const void* pointer_638 = &i;
+const void* pointer_639 = &i;
+const void* pointer_640 = &i;
+const void* pointer_641 = &i;
+const void* pointer_642 = &i;
+const void* pointer_643 = &i;
+const void* pointer_644 = &i;
+const void* pointer_645 = &i;
+const void* pointer_646 = &i;
+const void* pointer_647 = &i;
+const void* pointer_648 = &i;
+const void* pointer_649 = &i;
+const void* pointer_650 = &i;
+const void* pointer_651 = &i;
+const void* pointer_652 = &i;
+const void* pointer_653 = &i;
+const void* pointer_654 = &i;
+const void* pointer_655 = &i;
+const void* pointer_656 = &i;
+const void* pointer_657 = &i;
+const void* pointer_658 = &i;
+const void* pointer_659 = &i;
+const void* pointer_660 = &i;
+const void* pointer_661 = &i;
+const void* pointer_662 = &i;
+const void* pointer_663 = &i;
+const void* pointer_664 = &i;
+const void* pointer_665 = &i;
+const void* pointer_666 = &i;
+const void* pointer_667 = &i;
+const void* pointer_668 = &i;
+const void* pointer_669 = &i;
+const void* pointer_670 = &i;
+const void* pointer_671 = &i;
+const void* pointer_672 = &i;
+const void* pointer_673 = &i;
+const void* pointer_674 = &i;
+const void* pointer_675 = &i;
+const void* pointer_676 = &i;
+const void* pointer_677 = &i;
+const void* pointer_678 = &i;
+const void* pointer_679 = &i;
+const void* pointer_680 = &i;
+const void* pointer_681 = &i;
+const void* pointer_682 = &i;
+const void* pointer_683 = &i;
+const void* pointer_684 = &i;
+const void* pointer_685 = &i;
+const void* pointer_686 = &i;
+const void* pointer_687 = &i;
+const void* pointer_688 = &i;
+const void* pointer_689 = &i;
+const void* pointer_690 = &i;
+const void* pointer_691 = &i;
+const void* pointer_692 = &i;
+const void* pointer_693 = &i;
+const void* pointer_694 = &i;
+const void* pointer_695 = &i;
+const void* pointer_696 = &i;
+const void* pointer_697 = &i;
+const void* pointer_698 = &i;
+const void* pointer_699 = &i;
+const void* pointer_700 = &i;
+const void* pointer_701 = &i;
+const void* pointer_702 = &i;
+const void* pointer_703 = &i;
+const void* pointer_704 = &i;
+const void* pointer_705 = &i;
+const void* pointer_706 = &i;
+const void* pointer_707 = &i;
+const void* pointer_708 = &i;
+const void* pointer_709 = &i;
+const void* pointer_710 = &i;
+const void* pointer_711 = &i;
+const void* pointer_712 = &i;
+const void* pointer_713 = &i;
+const void* pointer_714 = &i;
+const void* pointer_715 = &i;
+const void* pointer_716 = &i;
+const void* pointer_717 = &i;
+const void* pointer_718 = &i;
+const void* pointer_719 = &i;
+const void* pointer_720 = &i;
+const void* pointer_721 = &i;
+const void* pointer_722 = &i;
+const void* pointer_723 = &i;
+const void* pointer_724 = &i;
+const void* pointer_725 = &i;
+const void* pointer_726 = &i;
+const void* pointer_727 = &i;
+const void* pointer_728 = &i;
+const void* pointer_729 = &i;
+const void* pointer_730 = &i;
+const void* pointer_731 = &i;
+const void* pointer_732 = &i;
+const void* pointer_733 = &i;
+const void* pointer_734 = &i;
+const void* pointer_735 = &i;
+const void* pointer_736 = &i;
+const void* pointer_737 = &i;
+const void* pointer_738 = &i;
+const void* pointer_739 = &i;
+const void* pointer_740 = &i;
+const void* pointer_741 = &i;
+const void* pointer_742 = &i;
+const void* pointer_743 = &i;
+const void* pointer_744 = &i;
+const void* pointer_745 = &i;
+const void* pointer_746 = &i;
+const void* pointer_747 = &i;
+const void* pointer_748 = &i;
+const void* pointer_749 = &i;
+const void* pointer_750 = &i;
+const void* pointer_751 = &i;
+const void* pointer_752 = &i;
+const void* pointer_753 = &i;
+const void* pointer_754 = &i;
+const void* pointer_755 = &i;
+const void* pointer_756 = &i;
+const void* pointer_757 = &i;
+const void* pointer_758 = &i;
+const void* pointer_759 = &i;
+const void* pointer_760 = &i;
+const void* pointer_761 = &i;
+const void* pointer_762 = &i;
+const void* pointer_763 = &i;
+const void* pointer_764 = &i;
+const void* pointer_765 = &i;
+const void* pointer_766 = &i;
+const void* pointer_767 = &i;
+const void* pointer_768 = &i;
+const void* pointer_769 = &i;
+const void* pointer_770 = &i;
+const void* pointer_771 = &i;
+const void* pointer_772 = &i;
+const void* pointer_773 = &i;
+const void* pointer_774 = &i;
+const void* pointer_775 = &i;
+const void* pointer_776 = &i;
+const void* pointer_777 = &i;
+const void* pointer_778 = &i;
+const void* pointer_779 = &i;
+const void* pointer_780 = &i;
+const void* pointer_781 = &i;
+const void* pointer_782 = &i;
+const void* pointer_783 = &i;
+const void* pointer_784 = &i;
+const void* pointer_785 = &i;
+const void* pointer_786 = &i;
+const void* pointer_787 = &i;
+const void* pointer_788 = &i;
+const void* pointer_789 = &i;
+const void* pointer_790 = &i;
+const void* pointer_791 = &i;
+const void* pointer_792 = &i;
+const void* pointer_793 = &i;
+const void* pointer_794 = &i;
+const void* pointer_795 = &i;
+const void* pointer_796 = &i;
+const void* pointer_797 = &i;
+const void* pointer_798 = &i;
+const void* pointer_799 = &i;
+const void* pointer_800 = &i;
+const void* pointer_801 = &i;
+const void* pointer_802 = &i;
+const void* pointer_803 = &i;
+const void* pointer_804 = &i;
+const void* pointer_805 = &i;
+const void* pointer_806 = &i;
+const void* pointer_807 = &i;
+const void* pointer_808 = &i;
+const void* pointer_809 = &i;
+const void* pointer_810 = &i;
+const void* pointer_811 = &i;
+const void* pointer_812 = &i;
+const void* pointer_813 = &i;
+const void* pointer_814 = &i;
+const void* pointer_815 = &i;
+const void* pointer_816 = &i;
+const void* pointer_817 = &i;
+const void* pointer_818 = &i;
+const void* pointer_819 = &i;
+const void* pointer_820 = &i;
+const void* pointer_821 = &i;
+const void* pointer_822 = &i;
+const void* pointer_823 = &i;
+const void* pointer_824 = &i;
+const void* pointer_825 = &i;
+const void* pointer_826 = &i;
+const void* pointer_827 = &i;
+const void* pointer_828 = &i;
+const void* pointer_829 = &i;
+const void* pointer_830 = &i;
+const void* pointer_831 = &i;
+const void* pointer_832 = &i;
+const void* pointer_833 = &i;
+const void* pointer_834 = &i;
+const void* pointer_835 = &i;
+const void* pointer_836 = &i;
+const void* pointer_837 = &i;
+const void* pointer_838 = &i;
+const void* pointer_839 = &i;
+const void* pointer_840 = &i;
+const void* pointer_841 = &i;
+const void* pointer_842 = &i;
+const void* pointer_843 = &i;
+const void* pointer_844 = &i;
+const void* pointer_845 = &i;
+const void* pointer_846 = &i;
+const void* pointer_847 = &i;
+const void* pointer_848 = &i;
+const void* pointer_849 = &i;
+const void* pointer_850 = &i;
+const void* pointer_851 = &i;
+const void* pointer_852 = &i;
+const void* pointer_853 = &i;
+const void* pointer_854 = &i;
+const void* pointer_855 = &i;
+const void* pointer_856 = &i;
+const void* pointer_857 = &i;
+const void* pointer_858 = &i;
+const void* pointer_859 = &i;
+const void* pointer_860 = &i;
+const void* pointer_861 = &i;
+const void* pointer_862 = &i;
+const void* pointer_863 = &i;
+const void* pointer_864 = &i;
+const void* pointer_865 = &i;
+const void* pointer_866 = &i;
+const void* pointer_867 = &i;
+const void* pointer_868 = &i;
+const void* pointer_869 = &i;
+const void* pointer_870 = &i;
+const void* pointer_871 = &i;
+const void* pointer_872 = &i;
+const void* pointer_873 = &i;
+const void* pointer_874 = &i;
+const void* pointer_875 = &i;
+const void* pointer_876 = &i;
+const void* pointer_877 = &i;
+const void* pointer_878 = &i;
+const void* pointer_879 = &i;
+const void* pointer_880 = &i;
+const void* pointer_881 = &i;
+const void* pointer_882 = &i;
+const void* pointer_883 = &i;
+const void* pointer_884 = &i;
+const void* pointer_885 = &i;
+const void* pointer_886 = &i;
+const void* pointer_887 = &i;
+const void* pointer_888 = &i;
+const void* pointer_889 = &i;
+const void* pointer_890 = &i;
+const void* pointer_891 = &i;
+const void* pointer_892 = &i;
+const void* pointer_893 = &i;
+const void* pointer_894 = &i;
+const void* pointer_895 = &i;
+const void* pointer_896 = &i;
+const void* pointer_897 = &i;
+const void* pointer_898 = &i;
+const void* pointer_899 = &i;
+const void* pointer_900 = &i;
+const void* pointer_901 = &i;
+const void* pointer_902 = &i;
+const void* pointer_903 = &i;
+const void* pointer_904 = &i;
+const void* pointer_905 = &i;
+const void* pointer_906 = &i;
+const void* pointer_907 = &i;
+const void* pointer_908 = &i;
+const void* pointer_909 = &i;
+const void* pointer_910 = &i;
+const void* pointer_911 = &i;
+const void* pointer_912 = &i;
+const void* pointer_913 = &i;
+const void* pointer_914 = &i;
+const void* pointer_915 = &i;
+const void* pointer_916 = &i;
+const void* pointer_917 = &i;
+const void* pointer_918 = &i;
+const void* pointer_919 = &i;
+const void* pointer_920 = &i;
+const void* pointer_921 = &i;
+const void* pointer_922 = &i;
+const void* pointer_923 = &i;
+const void* pointer_924 = &i;
+const void* pointer_925 = &i;
+const void* pointer_926 = &i;
+const void* pointer_927 = &i;
+const void* pointer_928 = &i;
+const void* pointer_929 = &i;
+const void* pointer_930 = &i;
+const void* pointer_931 = &i;
+const void* pointer_932 = &i;
+const void* pointer_933 = &i;
+const void* pointer_934 = &i;
+const void* pointer_935 = &i;
+const void* pointer_936 = &i;
+const void* pointer_937 = &i;
+const void* pointer_938 = &i;
+const void* pointer_939 = &i;
+const void* pointer_940 = &i;
+const void* pointer_941 = &i;
+const void* pointer_942 = &i;
+const void* pointer_943 = &i;
+const void* pointer_944 = &i;
+const void* pointer_945 = &i;
+const void* pointer_946 = &i;
+const void* pointer_947 = &i;
+const void* pointer_948 = &i;
+const void* pointer_949 = &i;
+const void* pointer_950 = &i;
+const void* pointer_951 = &i;
+const void* pointer_952 = &i;
+const void* pointer_953 = &i;
+const void* pointer_954 = &i;
+const void* pointer_955 = &i;
+const void* pointer_956 = &i;
+const void* pointer_957 = &i;
+const void* pointer_958 = &i;
+const void* pointer_959 = &i;
+const void* pointer_960 = &i;
+const void* pointer_961 = &i;
+const void* pointer_962 = &i;
+const void* pointer_963 = &i;
+const void* pointer_964 = &i;
+const void* pointer_965 = &i;
+const void* pointer_966 = &i;
+const void* pointer_967 = &i;
+const void* pointer_968 = &i;
+const void* pointer_969 = &i;
+const void* pointer_970 = &i;
+const void* pointer_971 = &i;
+const void* pointer_972 = &i;
+const void* pointer_973 = &i;
+const void* pointer_974 = &i;
+const void* pointer_975 = &i;
+const void* pointer_976 = &i;
+const void* pointer_977 = &i;
+const void* pointer_978 = &i;
+const void* pointer_979 = &i;
+const void* pointer_980 = &i;
+const void* pointer_981 = &i;
+const void* pointer_982 = &i;
+const void* pointer_983 = &i;
+const void* pointer_984 = &i;
+const void* pointer_985 = &i;
+const void* pointer_986 = &i;
+const void* pointer_987 = &i;
+const void* pointer_988 = &i;
+const void* pointer_989 = &i;
+const void* pointer_990 = &i;
+const void* pointer_991 = &i;
+const void* pointer_992 = &i;
+const void* pointer_993 = &i;
+const void* pointer_994 = &i;
+const void* pointer_995 = &i;
+const void* pointer_996 = &i;
+const void* pointer_997 = &i;
+const void* pointer_998 = &i;
+const void* pointer_999 = &i;
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
new file mode 100755
index 0000000..6ce6d0c
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
new file mode 100755
index 0000000..d97ef82
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
new file mode 100755
index 0000000..945b450
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
new file mode 100755
index 0000000..e44e459
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
new file mode 100755
index 0000000..e71b5cb
--- /dev/null
+++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build relocation packer unit test data.
+
+Uses a built relocation packer to generate 'golden' reference test data
+files for elf_file_unittests.cc.
+"""
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+def PackArmLibraryRelocations(android_pack_relocations,
+ android_objcopy,
+ added_section,
+ input_path,
+ output_path):
+ # Copy and add a 'NULL' .android.rel.dyn section for the packing tool.
+ with tempfile.NamedTemporaryFile() as stream:
+ stream.write('NULL')
+ stream.flush()
+ objcopy_command = [android_objcopy,
+ '--add-section', '%s=%s' % (added_section, stream.name),
+ input_path, output_path]
+ subprocess.check_call(objcopy_command)
+
+ # Pack relocations.
+ pack_command = [android_pack_relocations, output_path]
+ subprocess.check_call(pack_command)
+
+
+def UnpackArmLibraryRelocations(android_pack_relocations,
+ input_path,
+ output_path):
+ shutil.copy(input_path, output_path)
+
+ # Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn
+ # in place.
+ unpack_command = [android_pack_relocations, '-u', output_path]
+ subprocess.check_call(unpack_command)
+
+
+def main():
+ parser = optparse.OptionParser()
+
+ parser.add_option('--android-pack-relocations',
+ help='Path to the ARM relocations packer binary')
+ parser.add_option('--android-objcopy',
+ help='Path to the toolchain\'s objcopy binary')
+ parser.add_option('--added-section',
+ choices=['.android.rel.dyn', '.android.rela.dyn'],
+ help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"')
+ parser.add_option('--test-file',
+ help='Path to the input test file, an unpacked ARM .so')
+ parser.add_option('--unpacked-output',
+ help='Path to the output file for reference unpacked data')
+ parser.add_option('--packed-output',
+ help='Path to the output file for reference packed data')
+
+ options, _ = parser.parse_args()
+
+ for output in [options.unpacked_output, options.packed_output]:
+ directory = os.path.dirname(output)
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ PackArmLibraryRelocations(options.android_pack_relocations,
+ options.android_objcopy,
+ options.added_section,
+ options.test_file,
+ options.packed_output)
+
+ UnpackArmLibraryRelocations(options.android_pack_relocations,
+ options.packed_output,
+ options.unpacked_output)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
new file mode 100755
index 0000000..f90a2f6
--- /dev/null
+++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files
+# from elf_file_unittest_relocs.cc. Run once to create these test data
+# files; the files are checked into the source tree.
+#
+# To use:
+# ./generate_elf_file_unittest_relocs.sh
+# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so
+
+function main() {
+ local '-r' test_data_directory="$(pwd)"
+ cd '../../..'
+
+ source tools/cr/cr-bash-helpers.sh
+ local arch
+ for arch in 'arm32' 'arm64'; do
+ cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}"
+ cr 'build' 'relocation_packer_unittests_test_data'
+ done
+
+ local '-r' packer='out_android/Debug/obj/tools/relocation_packer'
+ local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen"
+
+ cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \
+ "${test_data_directory}"
+
+ return 0
+}
+
+main