linux-user/vdso: reproducible?
Hi!
Once again I'm trying to fix a long-standing issue in debian & ubuntu with vdso files build.
One of the requirements here is to build everything from source, ensuring the things can be rebuild and are reproducible (that is, the shipped binaries can actually be produced from the provided source and not something else with a hidden backdoor).
It'd be nice to just build the vdso.so files during regular package build. But this does not work correctly neither in debian nor in ubuntu infrastructure due to several reasons. The main problem is that to build all vdso files, one need to have cross-compilers for all architectures on every host architecture for which qemu-user packages are produced, which is problematic (especially for lower-power, or "small", architectures).
Additionally, llvm/clang, which can produce binaries for multiple architectures and is available on all host architectures on debian, does not handle sh4 and hpps which are currently needed to rebuild vdso files in qemu, so it is also not an option, at least not yet (sh4 support hopefully will be available soon).
I'm using tricks to build vdso files on one architecture and use the result when building linux-user packages on all architectures. But this does not work correctly neither in debian nor in ubuntu.
An alternative is to actually use the binaries provided in qemu source tarball. However in order to do that, I have to actually verify if the binaries are produced from the actual source shipped with these binaries. It doesn't seem to be a difficult task because of the similicity of the sources - it is a simple assembler-language file which - in theory - should be trivially reproducible.
However, when I build a vdso file locally with the same compiler options and compare it with the binary shipped with qemu, I see this:
linux-user/aarch64 $ diffoscope vdso-be.so*
2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.
2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.
--- vdso-be.so
+++ vdso-be.so.mjt
├── readelf --wide --symbols {}
│ @@ -19,15 +19,15 @@
│ 6: 0000000000000380 0 SECTION LOCAL DEFAULT 6 .gnu.hash
│ 7: 00000000000003b8 0 SECTION LOCAL DEFAULT 7 .dynstr
│ 8: 0000000000000430 0 SECTION LOCAL DEFAULT 8 .gnu.version
│ 9: 0000000000000440 0 SECTION LOCAL DEFAULT 9 .gnu.version_d
│ 10: 0000000000000478 0 SECTION LOCAL DEFAULT 10 .eh_frame_hdr
│ 11: 0000000000000490 0 SECTION LOCAL DEFAULT 11 .eh_frame
│ 12: 00000000000004b8 0 SECTION LOCAL DEFAULT 12 .text
│ - 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS cc0Lcfxq.o
│ + 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS ccuckKYd.o
│ 14: 0000000000000158 0 NOTYPE LOCAL DEFAULT 1 $d
│ 15: 00000000000004b8 0 NOTYPE LOCAL DEFAULT 12 $x
│ 16: 00000000000004f0 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_start
│ 17: 00000000000004f4 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_end
│ 18: 00000000000004a4 0 NOTYPE LOCAL DEFAULT 11 $d
│ 19: 0000000000000000 0 FILE LOCAL DEFAULT ABS
│ 20: 00000000000001a0 0 OBJECT LOCAL DEFAULT ABS _DYNAMIC
├── strings --all --bytes=8 {}
│ @@ -1,14 +1,14 @@
│ __kernel_gettimeofday
│ __kernel_clock_gettime
│ __kernel_clock_getres
│ __kernel_rt_sigreturn
│ linux-vdso.so.1
│ LINUX_2.6.39
│ -cc0Lcfxq.o
│ +ccuckKYd.o
│ sigreturn_region_start
│ sigreturn_region_end
│ _DYNAMIC
│ __GNU_EH_FRAME_HDR
│ _GLOBAL_OFFSET_TABLE_
│ LINUX_2.6.39
│ __kernel_clock_getres
├── readelf --wide --decompress --hex-dump=.strtab {}
│ @@ -1,10 +1,10 @@
│
│ Hex dump of section '.strtab':
│ - 0x00000000 00636330 4c636678 712e6f00 24640024 .cc0Lcfxq.o.$d.$
│ + 0x00000000 00636375 636b4b59 642e6f00 24640024 .ccuckKYd.o.$d.$
│ 0x00000010 78007369 67726574 75726e5f 72656769 x.sigreturn_regi
│ 0x00000020 6f6e5f73 74617274 00736967 72657475 on_start.sigretu
│ 0x00000030 726e5f72 6567696f 6e5f656e 64005f44 rn_region_end._D
│ 0x00000040 594e414d 4943005f 5f474e55 5f45485f YNAMIC.__GNU_EH_
│ 0x00000050 4652414d 455f4844 52005f47 4c4f4241 FRAME_HDR._GLOBA
│ 0x00000060 4c5f4f46 46534554 5f544142 4c455f00 L_OFFSET_TABLE_.
│ 0x00000070 4c494e55 585f322e 362e3339 005f5f6b LINUX_2.6.39.__kSo basically, the difference is just in the temporary .o filename used during the build process, and that name is random.
After stripping both files from debug info (aarch64-linux-gnu-strip), things becomes exactly the same.
Can't we strip them during build anyway?
(I'm not an expert with these reproducible build thing, maybe they have more ways to handle differences like this, and maybe more suggestions to further improve the situation besides stripping - asked them too).
It's interesting that adding -s switch to gcc produces slightly different binary than not using -s and stripping after the link explicitly.
linux-user/aarch64$ diffoscope --tool-prefix-binutils=aarch64-linux-gnu- vdso-be.so*
2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.
2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.
--- vdso-be.so
+++ vdso-be.so.mjt
│┄ xxd not available in path. Falling back to Python hexlify.
│┄ Format-specific differences are supported for ELF binaries but no file-specific differences were detected; falling back to a binary diff. file(1) reports: ELF 64-bit MSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ba5dd46a2ef8dde79536f81dd8126e0ee2f3df39, stripped@@ -6,15 +6,15 @@
00000000000004f40000000000001000000000020000000400000000000001a0 00000000000001a000000000000001a000000000000001000000000000000100 00000000000000086474e5500000000400000000000004780000000000000478 0000000000000478000000000000001400000000000000140000000000000004 0000000400000004000000000000015800000000000001580000000000000158 0000000000000044000000000000004400000000000000080000000400000010 00000005474e5500c00000000000000400000003000000000000000400000014
-00000003474e5500ba5dd46a2ef8dde79536f81dd8126e0ee2f3df3900000000 +00000003474e550047654bef50c6d25c99110c67c0eddb8e67b78bab00000000
000000000000000e000000000000005a00000000000000040000000000000350 000000006ffffef50000000000000380000000000000000500000000000003b8 000000000000000600000000000002a0000000000000000a0000000000000077 000000000000000b0000000000000018000000006ffffffc0000000000000440 000000006ffffffd0000000000000002000000006ffffff00000000000000430 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
So, can we at least strip the files?
Something like this (for all files):
linux-user/aarch64/Makefile.vdso +4 -2
@@ -10,7 +10,9 @@ LDFLAGS = -nostdlib -shared -Wl,-h,linux-vdso.so.1 \ -Wl,-z,max-page-size=4096 -Wl,-T,$(SUBDIR)/vdso.ld $(SUBDIR)/vdso-be.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mbig-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@ $(SUBDIR)/vdso-le.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mlittle-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@
Or maybe we can use something else to make this "more reproducible".
Thanks,
/mjt
Quoted 17 lines · Michael Tokarev
Hi!
Once again I'm trying to fix a long-standing issue in debian & ubuntu with vdso files build.
One of the requirements here is to build everything from source, ensuring the things can be rebuild and are reproducible (that is, the shipped binaries can actually be produced from the provided source and not something else with a hidden backdoor).
It'd be nice to just build the vdso.so files during regular package build. But this does not work correctly neither in debian nor in ubuntu infrastructure due to several reasons. The main problem is that to build all vdso files, one need to have cross-compilers for all architectures on every host architecture for which qemu-user packages are produced, which is problematic (especially for lower-power, or "small", architectures).
Quoted 3 lines
Additionally, llvm/clang, which can produce binaries for multiple architectures and is available on all host architectures on debian, does not handle sh4 and hpps which are currently needed to rebuild vdso files in qemu, so it is also
I assume you meant "hppa" instead of "hpps".
Quoted 70 lines
not an option, at least not yet (sh4 support hopefully will be available soon).
I'm using tricks to build vdso files on one architecture and use the result when building linux-user packages on all architectures. But this does not work correctly neither in debian nor in ubuntu.
An alternative is to actually use the binaries provided in qemu source tarball. However in order to do that, I have to actually verify if the binaries are produced from the actual source shipped with these binaries. It doesn't seem to be a difficult task because of the similicity of the sources - it is a simple assembler-language file which - in theory - should be trivially reproducible.
However, when I build a vdso file locally with the same compiler options and compare it with the binary shipped with qemu, I see this:
linux-user/aarch64 $ diffoscope vdso-be.so*
2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1. 2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.vdso-be.so.mjt renamed from vdso-be.so +0
-0├── readelf --wide --symbols {} │ @@ -19,15 +19,15 @@ │ 6: 0000000000000380 0 SECTION LOCAL DEFAULT 6 .gnu.hash │ 7: 00000000000003b8 0 SECTION LOCAL DEFAULT 7 .dynstr │ 8: 0000000000000430 0 SECTION LOCAL DEFAULT 8 .gnu.version │ 9: 0000000000000440 0 SECTION LOCAL DEFAULT 9 .gnu.version_d │ 10: 0000000000000478 0 SECTION LOCAL DEFAULT 10 .eh_frame_hdr │ 11: 0000000000000490 0 SECTION LOCAL DEFAULT 11 .eh_frame │ 12: 00000000000004b8 0 SECTION LOCAL DEFAULT 12 .text │ - 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS cc0Lcfxq.o │ + 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS ccuckKYd.o │ 14: 0000000000000158 0 NOTYPE LOCAL DEFAULT 1 $d │ 15: 00000000000004b8 0 NOTYPE LOCAL DEFAULT 12 $x │ 16: 00000000000004f0 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_start │ 17: 00000000000004f4 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_end │ 18: 00000000000004a4 0 NOTYPE LOCAL DEFAULT 11 $d │ 19: 0000000000000000 0 FILE LOCAL DEFAULT ABS │ 20: 00000000000001a0 0 OBJECT LOCAL DEFAULT ABS _DYNAMIC ├── strings --all --bytes=8 {} │ @@ -1,14 +1,14 @@ │ __kernel_gettimeofday │ __kernel_clock_gettime │ __kernel_clock_getres │ __kernel_rt_sigreturn │ linux-vdso.so.1 │ LINUX_2.6.39 │ -cc0Lcfxq.o │ +ccuckKYd.o │ sigreturn_region_start │ sigreturn_region_end │ _DYNAMIC │ __GNU_EH_FRAME_HDR │ _GLOBAL_OFFSET_TABLE_ │ LINUX_2.6.39 │ __kernel_clock_getres ├── readelf --wide --decompress --hex-dump=.strtab {} │ @@ -1,10 +1,10 @@ │ │ Hex dump of section '.strtab': │ - 0x00000000 00636330 4c636678 712e6f00 24640024 .cc0Lcfxq.o.$d.$ │ + 0x00000000 00636375 636b4b59 642e6f00 24640024 .ccuckKYd.o.$d.$ │ 0x00000010 78007369 67726574 75726e5f 72656769 x.sigreturn_regi │ 0x00000020 6f6e5f73 74617274 00736967 72657475 on_start.sigretu │ 0x00000030 726e5f72 6567696f 6e5f656e 64005f44 rn_region_end._D │ 0x00000040 594e414d 4943005f 5f474e55 5f45485f YNAMIC.__GNU_EH_ │ 0x00000050 4652414d 455f4844 52005f47 4c4f4241 FRAME_HDR._GLOBA │ 0x00000060 4c5f4f46 46534554 5f544142 4c455f00 L_OFFSET_TABLE_. │ 0x00000070 4c494e55 585f322e 362e3339 005f5f6b LINUX_2.6.39.__k
Quoted 2 lines
So basically, the difference is just in the temporary .o filename used during the build process, and that name is random.
Is there a way to use a static name instead, e.g. tell the Makefile not to use a dynamic file name?
Quoted 4 lines
After stripping both files from debug info (aarch64-linux-gnu-strip), things becomes exactly the same.
Can't we strip them during build anyway?
If you strip them, you loose the ability to debug at runtime in the guest.
Quoted 54 lines
(I'm not an expert with these reproducible build thing, maybe they have more ways to handle differences like this, and maybe more suggestions to further improve the situation besides stripping - asked them too).
It's interesting that adding -s switch to gcc produces slightly different binary than not using -s and stripping after the link explicitly.
linux-user/aarch64$ diffoscope --tool-prefix-binutils=aarch64-linux-gnu- vdso-be.so*
2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1. 2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non-zero exit status 1.vdso-be.so.mjt renamed from vdso-be.so +0
-0│┄ xxd not available in path. Falling back to Python hexlify. │┄ Format-specific differences are supported for ELF binaries but no file-specific differences were detected; falling back to a binary diff. file(1) reports: ELF 64-bit MSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ba5dd46a2ef8dde79536f81dd8126e0ee2f3df39, strippedvdso-be.so.mjt renamed from vdso-be.so +1
-1@@ -6,15 +6,15 @@ 00000000000004f40000000000001000000000020000000400000000000001a0 00000000000001a000000000000001a000000000000001000000000000000100 00000000000000086474e5500000000400000000000004780000000000000478 0000000000000478000000000000001400000000000000140000000000000004 0000000400000004000000000000015800000000000001580000000000000158 0000000000000044000000000000004400000000000000080000000400000010 00000005474e5500c00000000000000400000003000000000000000400000014 -00000003474e5500ba5dd46a2ef8dde79536f81dd8126e0ee2f3df3900000000 +00000003474e550047654bef50c6d25c99110c67c0eddb8e67b78bab00000000 000000000000000e000000000000005a00000000000000040000000000000350 000000006ffffef50000000000000380000000000000000500000000000003b8 000000000000000600000000000002a0000000000000000a0000000000000077 000000000000000b0000000000000018000000006ffffffc0000000000000440 000000006ffffffd0000000000000002000000006ffffff00000000000000430 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000So, can we at least strip the files?
Something like this (for all files):
linux-user/aarch64/Makefile.vdso +4
-2@@ -10,7 +10,9 @@ LDFLAGS = -nostdlib -shared -Wl,-h,linux-vdso.so.1 \ -Wl,-z,max-page-size=4096 -Wl,-T,$(SUBDIR)/vdso.ld $(SUBDIR)/vdso-be.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mbig-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@ $(SUBDIR)/vdso-le.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mlittle-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@
Quoted 1 line
Or maybe we can use something else to make this "more reproducible".
No idea right now.
Helge
Quoted 18 lines · Helge Deller
Quoted 17 lines · Michael Tokarev
Hi!
Once again I'm trying to fix a long-standing issue in debian & ubuntu with vdso files build.
One of the requirements here is to build everything from source, ensuring the things can be rebuild and are reproducible (that is, the shipped binaries can actually be produced from the provided source and not something else with a hidden backdoor).
It'd be nice to just build the vdso.so files during regular package build. But this does not work correctly neither in debian nor in ubuntu infrastructure due to several reasons. The main problem is that to build all vdso files, one need to have cross-compilers for all architectures on every host architecture for which qemu-user packages are produced, which is problematic (especially for lower-power, or "small", architectures).
Quoted 5 lines
Quoted 3 lines
Additionally, llvm/clang, which can produce binaries for multiple architectures and is available on all host architectures on debian, does not handle sh4 and hpps which are currently needed to rebuild vdso files in qemu, so it is also
I assume you meant "hppa" instead of "hpps".
Yeah, just a typo.
Quoted 8 lines
Quoted 5 lines
│ -cc0Lcfxq.o │ +ccuckKYd.oSo basically, the difference is just in the temporary .o filename used during the build process, and that name is random.
Is there a way to use a static name instead, e.g. tell the
Makefilenot to use a dynamic file name?
Well yes - either use explicit separate compile and link command lines, or use -pipe (this eliminates this name entirely).
Quoted 6 lines
Quoted 4 lines
After stripping both files from debug info (aarch64-linux-gnu-strip), things becomes exactly the same.
Can't we strip them during build anyway?
If you strip them, you loose the ability to debug at runtime in the guest.
Sure. These object files contains very little code, it's mostly just a symbol table, nothing more. There's nothing to debug in there really.
Thanks,
/mjt
Quoted 31 lines · Michael Tokarev
Quoted 18 lines · Helge Deller
Quoted 17 lines · Michael Tokarev
Hi!
Once again I'm trying to fix a long-standing issue in debian & ubuntu with vdso files build.
One of the requirements here is to build everything from source, ensuring the things can be rebuild and are reproducible (that is, the shipped binaries can actually be produced from the provided source and not something else with a hidden backdoor).
It'd be nice to just build the vdso.so files during regular package build. But this does not work correctly neither in debian nor in ubuntu infrastructure due to several reasons. The main problem is that to build all vdso files, one need to have cross-compilers for all architectures on every host architecture for which qemu-user packages are produced, which is problematic (especially for lower-power, or "small", architectures).
Quoted 5 lines
Quoted 3 lines
Additionally, llvm/clang, which can produce binaries for multiple architectures and is available on all host architectures on debian, does not handle sh4 and hpps which are currently needed to rebuild vdso files in qemu, so it is also
I assume you meant "hppa" instead of "hpps".
Yeah, just a typo.
Quoted 2 lines
Quoted 2 lines
│ -cc0Lcfxq.o │ +ccuckKYd.o
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
So basically, the difference is just in the temporary .o filename used during the build process, and that name is random.
Is there a way to use a static name instead, e.g. tell the
Makefilenot to use a dynamic file name?Well yes - either use explicit separate compile and link command lines, or use -pipe (this eliminates this name entirely).
So -pipe is probably the best alternative of both.
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines
After stripping both files from debug info (aarch64-linux-gnu-strip), things becomes exactly the same.
Can't we strip them during build anyway?
If you strip them, you loose the ability to debug at runtime in the guest.
Sure. These object files contains very little code, it's mostly just a symbol table, nothing more. There's nothing to debug in there really.
True, but the symbol table resolves the entries to the VDSO while debugging.
Either way, if dropping the symbol table is the easiest way to resolve the reproduceable builds, then I'm ok with that too.
Helge
Adding Paolo to Cc. Can we at least add -pipe to the vdso compile command to avoid having a random filename embedded in the binary? For 11.2 perhaps?
Thanks,
/mjt
Quoted 158 lines · Michael Tokarev
Hi!
Once again I'm trying to fix a long-standing issue in debian & ubuntu with vdso files build.
One of the requirements here is to build everything from source, ensuring the things can be rebuild and are reproducible (that is, the shipped binaries can actually be produced from the provided source and not something else with a hidden backdoor).
It'd be nice to just build the vdso.so files during regular package build. But this does not work correctly neither in debian nor in ubuntu infrastructure due to several reasons. The main problem is that to build all vdso files, one need to have cross-compilers for all architectures on every host architecture for which qemu-user packages are produced, which is problematic (especially for lower-power, or "small", architectures).
Additionally, llvm/clang, which can produce binaries for multiple architectures and is available on all host architectures on debian, does not handle sh4 and hpps which are currently needed to rebuild vdso files in qemu, so it is also not an option, at least not yet (sh4 support hopefully will be available soon).
I'm using tricks to build vdso files on one architecture and use the result when building linux-user packages on all architectures. But this does not work correctly neither in debian nor in ubuntu.
An alternative is to actually use the binaries provided in qemu source tarball. However in order to do that, I have to actually verify if the binaries are produced from the actual source shipped with these binaries. It doesn't seem to be a difficult task because of the similicity of the sources - it is a simple assembler-language file which - in theory - should be trivially reproducible.
However, when I build a vdso file locally with the same compiler options and compare it with the binary shipped with qemu, I see this:
linux-user/aarch64 $ diffoscope vdso-be.so*
2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn -- section=.text {}']' returned non-zero exit status 1. 2026-10-23 18:59:46 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non- zero exit status 1.vdso-be.so.mjt renamed from vdso-be.so +0
-0├── readelf --wide --symbols {} │ @@ -19,15 +19,15 @@ │ 6: 0000000000000380 0 SECTION LOCAL DEFAULT 6 .gnu.hash │ 7: 00000000000003b8 0 SECTION LOCAL DEFAULT 7 .dynstr │ 8: 0000000000000430 0 SECTION LOCAL DEFAULT 8 .gnu.version │ 9: 0000000000000440 0 SECTION LOCAL DEFAULT 9 .gnu.version_d │ 10: 0000000000000478 0 SECTION LOCAL DEFAULT 10 .eh_frame_hdr │ 11: 0000000000000490 0 SECTION LOCAL DEFAULT 11 .eh_frame │ 12: 00000000000004b8 0 SECTION LOCAL DEFAULT 12 .text │ - 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS cc0Lcfxq.o │ + 13: 0000000000000000 0 FILE LOCAL DEFAULT ABS ccuckKYd.o │ 14: 0000000000000158 0 NOTYPE LOCAL DEFAULT 1 $d │ 15: 00000000000004b8 0 NOTYPE LOCAL DEFAULT 12 $x │ 16: 00000000000004f0 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_start │ 17: 00000000000004f4 0 NOTYPE LOCAL DEFAULT 12 sigreturn_region_end │ 18: 00000000000004a4 0 NOTYPE LOCAL DEFAULT 11 $d │ 19: 0000000000000000 0 FILE LOCAL DEFAULT ABS │ 20: 00000000000001a0 0 OBJECT LOCAL DEFAULT ABS _DYNAMIC ├── strings --all --bytes=8 {} │ @@ -1,14 +1,14 @@ │ __kernel_gettimeofday │ __kernel_clock_gettime │ __kernel_clock_getres │ __kernel_rt_sigreturn │ linux-vdso.so.1 │ LINUX_2.6.39 │ -cc0Lcfxq.o │ +ccuckKYd.o │ sigreturn_region_start │ sigreturn_region_end │ _DYNAMIC │ __GNU_EH_FRAME_HDR │ _GLOBAL_OFFSET_TABLE_ │ LINUX_2.6.39 │ __kernel_clock_getres ├── readelf --wide --decompress --hex-dump=.strtab {} │ @@ -1,10 +1,10 @@ │ │ Hex dump of section '.strtab': │ - 0x00000000 00636330 4c636678 712e6f00 24640024 .cc0Lcfxq.o.$d.$ │ + 0x00000000 00636375 636b4b59 642e6f00 24640024 .ccuckKYd.o.$d.$ │ 0x00000010 78007369 67726574 75726e5f 72656769 x.sigreturn_regi │ 0x00000020 6f6e5f73 74617274 00736967 72657475 on_start.sigretu │ 0x00000030 726e5f72 6567696f 6e5f656e 64005f44 rn_region_end._D │ 0x00000040 594e414d 4943005f 5f474e55 5f45485f YNAMIC.__GNU_EH_ │ 0x00000050 4652414d 455f4844 52005f47 4c4f4241 FRAME_HDR._GLOBA │ 0x00000060 4c5f4f46 46534554 5f544142 4c455f00 L_OFFSET_TABLE_. │ 0x00000070 4c494e55 585f322e 362e3339 005f5f6b LINUX_2.6.39.__kSo basically, the difference is just in the temporary .o filename used during the build process, and that name is random.
After stripping both files from debug info (aarch64-linux-gnu-strip), things becomes exactly the same.
Can't we strip them during build anyway?
(I'm not an expert with these reproducible build thing, maybe they have more ways to handle differences like this, and maybe more suggestions to further improve the situation besides stripping - asked them too).
It's interesting that adding -s switch to gcc produces slightly different binary than not using -s and stripping after the link explicitly.
linux-user/aarch64$ diffoscope --tool-prefix-binutils=aarch64-linux-gnu- vdso-be.so*
2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn -- section=.text {}']' returned non-zero exit status 1. 2026-10-23 19:06:01 E: diffoscope.comparators.elf: Command '['objdump --disassemble --demangle --no-show-raw-insn --section=.text {}']' returned non- zero exit status 1.vdso-be.so.mjt renamed from vdso-be.so +0
-0│┄ xxd not available in path. Falling back to Python hexlify. │┄ Format-specific differences are supported for ELF binaries but no file-specific differences were detected; falling back to a binary diff. file(1) reports: ELF 64-bit MSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ba5dd46a2ef8dde79536f81dd8126e0ee2f3df39, strippedvdso-be.so.mjt renamed from vdso-be.so +1
-1@@ -6,15 +6,15 @@ 00000000000004f40000000000001000000000020000000400000000000001a0 00000000000001a000000000000001a000000000000001000000000000000100 00000000000000086474e5500000000400000000000004780000000000000478 0000000000000478000000000000001400000000000000140000000000000004 0000000400000004000000000000015800000000000001580000000000000158 0000000000000044000000000000004400000000000000080000000400000010 00000005474e5500c00000000000000400000003000000000000000400000014 -00000003474e5500ba5dd46a2ef8dde79536f81dd8126e0ee2f3df3900000000 +00000003474e550047654bef50c6d25c99110c67c0eddb8e67b78bab00000000 000000000000000e000000000000005a00000000000000040000000000000350 000000006ffffef50000000000000380000000000000000500000000000003b8 000000000000000600000000000002a0000000000000000a0000000000000077 000000000000000b0000000000000018000000006ffffffc0000000000000440 000000006ffffffd0000000000000002000000006ffffff00000000000000430 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000So, can we at least strip the files?
Something like this (for all files):
linux-user/aarch64/Makefile.vdso +4
-2@@ -10,7 +10,9 @@ LDFLAGS = -nostdlib -shared -Wl,-h,linux-vdso.so.1 \ -Wl,-z,max-page-size=4096 -Wl,-T,$(SUBDIR)/vdso.ld $(SUBDIR)/vdso-be.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mbig-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@ $(SUBDIR)/vdso-le.so: vdso.S vdso.ld - $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< + $(CC) -o [email protected] $(LDFLAGS) -mlittle-endian $< + $(STRIP) [email protected] && mv -f [email protected] $@Or maybe we can use something else to make this "more reproducible".
Thanks,
/mjt