ptitSeb / box64

Box64 - Linux Userspace x86_64 Emulator with a twist, targeted at ARM64 Linux devices

Home Page:https://box86.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Box64 Seems Failing to Handle `.init_array`

Coekjan opened this issue · comments

Description

These days, I tried to run my simple x64 applications with box64 on rv64gc platform. Box64 did run my simple hello-world program with all used libraries wrapped (e.g. libc), but failed to run my hash program with some libraries emulated (e.g. libcrypto). Here is my hash program:

/* test.c */
#include <stdio.h>
#include <openssl/sha.h>

const char *hello = "Hello World";

int main() {
	unsigned char hash[SHA_DIGEST_LENGTH];
	SHA1((unsigned char *)hello, sizeof(hello) - 1, hash);
	for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
		printf("%02x", hash[i]);
	}
	return 0;
}

I compiled this program with gcc test.c -o test -lssl -lcrypto, and run BOX64_PREFER_EMULATED=1 ./box64 ./test. It emited the log:

the log
Dynarec for RISC-V With extension: I M A F D C Zba Zbb Zbc Zbs Vector PageSize:4096 Running on Unknown CPU with 16 Cores
Will use Hardware counter measured at 2.3 GHz
Params database has 14 entries
Box64 with Dynarec v0.2.9 cb064f9e built on Jun 29 2024 21:39:07
BOX64: Didn't detect 48bits of address space, considering it's 39bits
Counted 119 Env var
BOX64 LIB PATH: /usr/riscv64-linux-gnu/lib/:/lib/:./:lib/:lib64/:x86_64/:bin64/:libs64/
BOX64 BIN PATH: (( omitted ))
Looking for test
Rename process to "test"
Error initializing native libssl.so.3 (last dlerror is libssl.so.3: cannot open shared object file: No such file or directory)
Using emulated /lib/libssl.so.3
Error initializing native libcrypto.so.3 (last dlerror is libcrypto.so.3: cannot open shared object file: No such file or directory)
Using emulated /lib/libcrypto.so.3
Using native(wrapped) libc.so.6
Using native(wrapped) ld-linux-x86-64.so.2
Using native(wrapped) libpthread.so.0
Using native(wrapped) libdl.so.2
Using native(wrapped) libutil.so.1
Using native(wrapped) libresolv.so.2
Using native(wrapped) librt.so.1
Using native(wrapped) libbsd.so.0
FillBlock triggered a segfault at 0x4d100 from 0x3528682a
FillBlock at 0x4d100 triggered a segfault, canceling
393292|SIGSEGV @0x351b2a96 (???(test+0x9b2a96)) (x64pc=0x4d100/???:"???", rsp=0x7470313fe1f8, stack=0x747030c00000:0x747031400000 own=(nil) fp=0x7470313fe200), for accessing 0x4d100 (code=1/prot=87), db=(nil)((nil):(nil)/(nil):(nil)/???:clean, hash:0/0) handler=(nil)
RSP-0x20:0x00007470313fe200 RSP-0x18:0x0000000000000000 RSP-0x10:0x0000003f0104c01b RSP-0x08:0x0000000000000000
RSP+0x00:0x0000000000010080 RSP+0x08:0x0000000000000000 RSP+0x10:0x0000000000000001 RSP+0x18:0x00007470313fe6e1
RAX:0x0000000002d82203 RCX:0x0000000000000000 RDX:0x00007470313fe220 RBX:0x0000000000000000 
RSP:0x00007470313fe1f8 RBP:0x00007470313fe200 RSI:0x00007470313fe210 RDI:0x0000000000000001 
 R8:0x0000000000000000  R9:0x02d8220300000000 R10:0x0000000047888915 R11:0x000000000000000f 
R12:0x0000000000000000 R13:0x0000000000000000 R14:0x0000000000000000 R15:0x0000000000000000 
ES:0x002b CS:0x0033 SS:0x002b DS:0x002b FS:0x0043 GS:0x0053
the log (with `BOX64_LOG=2`)
Debug level is 2
Dynarec for RISC-V With extension: I M A F D C Zba Zbb Zbc Zbs Vector PageSize:4096 Running on Unknown CPU with 16 Cores
Will use Hardware counter measured at 2.3 GHz
Params database has 14 entries
Box64 with Dynarec v0.2.9 cb064f9e built on Jun 29 2024 21:39:07
BOX64: Didn't detect 48bits of address space, considering it's 39bits
Setting up canary (for Stack protector) at FS:0x28, value:91314800
Counted 119 Env var
 Env... (( omitted ))
BOX64 LIB PATH: /usr/riscv64-linux-gnu/lib/:/lib/:./:lib/:lib64/:x86_64/:bin64/:libs64/
BOX64 BIN PATH: (( omitted ))
Looking for ./test
Read 30 Section header
Read 13 Program header
Loading Sections Table String (idx = 29)
Loading SymTab Strings (idx = 28)
Loading SymTab (idx = 27)
Loading Dynamic (idx = 21)
The DT_INIT is at address 0x1000
The DT_FINI is at address 0x11dc
The DT_INIT_ARRAY is at address 0x3db0
The DT_INIT_ARRAYSZ is 1
The DT_FINI_ARRAY is at address 0x3db8
The DT_FINI_ARRAYSZ is 1
The DT_GNU_HASH is at address 0x3c0
The DT_VERNEED is at address 0x5a8
The DT_VERNEEDNUM is 2
RelA Table @0x608 (0xd8/0x18)
PLT Table @0x6e0 (type=7 0x48/0x18)
The GOT.PLT Table is at address 0x3fe8
The GOT Table is at address 0x3fc0..0x3fe8
The PLT Table is at address 0x1020..0x1060
The .gnu.version is at address 0x590
The .text is at address 0x1060, and is 377 big
The .eh_frame section is at address 0x2040..0x20bc
The .eh_frame_hdr section is at address 0x2018
Loading DynSym Strings (idx = 7)
Loading DynSym (idx = 6)
Adding "(( omitted ))/test" as #0 in elf collection
Elf Addr(v/p)=(nil)/(nil) Memsize=0x4038 (align=0x1000)
Elf Stack Memsize=1048576 (align=16)
Elf TLS Memsize=0 (align=0)
Pre-allocated 0x4038 byte at 0x100000000 for (( omitted ))/test
Delta of 0x100000000 (vaddr=(nil)) for Elf "(( omitted ))/test"
Mmaping 0x728(0x1000) bytes @0x100000000 for Elf "(( omitted ))/test"
Mmaping 0x1e9(0x1000) bytes @0x100001000 for Elf "(( omitted ))/test"
Mmaping 0xbc(0x1000) bytes @0x100002000 for Elf "(( omitted ))/test"
Allocating 0x3000 (0x100003000/0x288) bytes @0x100003000, will read 0x280 @0x100003db0 for Elf "(( omitted ))/test"
Rename process to "test"
Program linked with GLIBC 2.34+
Calc stack size, based on 1 elf(s)
Stack is @0x765a8b800000 size=0x800000 align=0x10
Allocate a new X86_64 Emu, with EIP=(nil) and Stack=0x765a8b800000/0x800000
Setup X86_64 Emu
Grabbing R_X86_64_COPY Relocation(s) in advance for (( omitted ))/test
Trying to add "libssl.so.3" to maplib
Trying to load "libssl.so.3"
Simplified name is "libssl.so.3"
Error initializing native libssl.so.3 (last dlerror is libssl.so.3: cannot open shared object file: No such file or directory)
Read 27 Section header
Read 11 Program header
Loading Sections Table String (idx = 26)
Loading SymTab Strings (idx = 0)
Loading SymTab (idx = 0)
Loading Dynamic (idx = 20)
The DT_INIT is at address 0x13000
The DT_FINI is at address 0xa0da4
The DT_INIT_ARRAY is at address 0xca730
The DT_INIT_ARRAYSZ is 1
The DT_FINI_ARRAY is at address 0xca738
The DT_FINI_ARRAYSZ is 1
The DT_GNU_HASH is at address 0x310
The DT_VERDEF is at address 0xf138
The DT_VERDEFNUM is 4
The DT_FLAGS is 0xa
The DT_VERNEED is at address 0xf1b8
The DT_VERNEEDNUM is 2
RelA Table @0xf2a8 (0x3768/0x18)
The GOT Table is at address 0xd3e50..0xd5000
The .gnu.version is at address 0xe82e
The .text is at address 0x13020, and is 580995 big
The .eh_frame section is at address 0xb7668..0xc9600
The .eh_frame_hdr section is at address 0xb4548
Loading DynSym Strings (idx = 5)
Loading DynSym (idx = 4)
Elf Addr(v/p)=(nil)/(nil) Memsize=0xd8770 (align=0x1000)
Elf Stack Memsize=1048576 (align=16)
Elf TLS Memsize=0 (align=0)
Pre-allocated 0xd8770 byte at 0x3f00000000 for /lib/libssl.so.3
Delta of 0x3f00000000 (vaddr=(nil)) for Elf "/lib/libssl.so.3"
Mmaping 0x12d68(0x13000) bytes @0x3f00000000 for Elf "/lib/libssl.so.3"
Mmaping 0x8ddb1(0x8e000) bytes @0x3f00013000 for Elf "/lib/libssl.so.3"
Mmaping 0x28600(0x29000) bytes @0x3f000a1000 for Elf "/lib/libssl.so.3"
Allocating 0x10000 (0x3f000ca000/0xe040) bytes @0x3f000ca000, will read 0xe000 @0x3f000ca730 for Elf "/lib/libssl.so.3"
Adding "/lib/libssl.so.3" as #1 in elf collection
Using emulated /lib/libssl.so.3
Trying to add "libcrypto.so.3" to maplib
Trying to load "libcrypto.so.3"
Simplified name is "libcrypto.so.3"
Error initializing native libcrypto.so.3 (last dlerror is libcrypto.so.3: cannot open shared object file: No such file or directory)
Read 27 Section header
Read 11 Program header
Loading Sections Table String (idx = 26)
Loading SymTab Strings (idx = 0)
Loading SymTab (idx = 0)
Loading Dynamic (idx = 20)
The DT_INIT is at address 0x4c000
The DT_FINI is at address 0x388d24
The DT_INIT_ARRAY is at address 0x465b90
The DT_INIT_ARRAYSZ is 1
The DT_FINI_ARRAY is at address 0x465b98
The DT_FINI_ARRAYSZ is 1
The DT_GNU_HASH is at address 0x310
The DT_VERDEF is at address 0x48da0
The DT_VERDEFNUM is 8
The DT_FLAGS is 0xa
The DT_VERNEED is at address 0x48eb0
The DT_VERNEEDNUM is 1
RelA Table @0x48fc0 (0xee8/0x18)
The GOT Table is at address 0x4c49f0..0x4c5000
The .gnu.version is at address 0x46156
The .text is at address 0x4d000, and is 3390755 big
The .eh_frame section is at address 0x3faa68..0x464a10
The .eh_frame_hdr section is at address 0x3e670c
Loading DynSym Strings (idx = 5)
Loading DynSym (idx = 4)
Elf Addr(v/p)=(nil)/(nil) Memsize=0x4cacc0 (align=0x1000)
Elf Stack Memsize=1048576 (align=16)
Elf TLS Memsize=0 (align=0)
Pre-allocated 0x4cacc0 byte at 0x3f01000000 for /lib/libcrypto.so.3
Delta of 0x3f01000000 (vaddr=(nil)) for Elf "/lib/libcrypto.so.3"
Mmaping 0x4b770(0x4c000) bytes @0x3f01000000 for Elf "/lib/libcrypto.so.3"
Mmaping 0x33cd31(0x33d000) bytes @0x3f0104c000 for Elf "/lib/libcrypto.so.3"
Mmaping 0xdba10(0xdc000) bytes @0x3f01389000 for Elf "/lib/libcrypto.so.3"
Allocating 0x67000 (0x3f01465000/0x65130) bytes @0x3f01465000, will read 0x61ff8 @0x3f01465b90 for Elf "/lib/libcrypto.so.3"
Adding "/lib/libcrypto.so.3" as #2 in elf collection
Using emulated /lib/libcrypto.so.3
Trying to add "libc.so.6" to maplib
Trying to load "libc.so.6"
Simplified name is "libc.so.6"
Using native(wrapped) libc.so.6
Trying to add "ld-linux-x86-64.so.2" to maplib
Trying to load "ld-linux-x86-64.so.2"
Simplified name is "ld-linux-x86-64.so.2"
Using native(wrapped) ld-linux-x86-64.so.2
Trying to add "libpthread.so.0" to maplib
Trying to load "libpthread.so.0"
Simplified name is "libpthread.so.0"
Using native(wrapped) libpthread.so.0
Trying to add "libdl.so.2" to maplib
Trying to load "libdl.so.2"
Simplified name is "libdl.so.2"
Using native(wrapped) libdl.so.2
Trying to add "libutil.so.1" to maplib
Trying to load "libutil.so.1"
Simplified name is "libutil.so.1"
Using native(wrapped) libutil.so.1
Trying to add "libresolv.so.2" to maplib
Trying to load "libresolv.so.2"
Simplified name is "libresolv.so.2"
Using native(wrapped) libresolv.so.2
Trying to add "librt.so.1" to maplib
Trying to load "librt.so.1"
Simplified name is "librt.so.1"
Using native(wrapped) librt.so.1
Trying to add "libbsd.so.0" to maplib
Trying to load "libbsd.so.0"
Simplified name is "libbsd.so.0"
Using native(wrapped) libbsd.so.0
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Trying to add "libc.so.6" to maplib
Already present in maplib => success
Created lib and added to maplib => success
Forcing /lib/libcrypto.so.3 to Bind Now
Applying 159 Relocation(s) with Addend for /lib/libcrypto.so.3 bindnow=1, deepbind=0
Forcing /lib/libcrypto.so.3 to Bind Now
Created lib and added to maplib => success
Trying to add "libcrypto.so.3" to maplib
Already present in maplib => success
Trying to add "libc.so.6" to maplib
Already present in maplib => success
Created lib and added to maplib => success
Created lib and added to maplib => success
Forcing /lib/libssl.so.3 to Bind Now
Applying 591 Relocation(s) with Addend for /lib/libssl.so.3 bindnow=1, deepbind=0
Forcing /lib/libssl.so.3 to Bind Now
Created lib and added to maplib => success
And now export symbols / relocation for (( omitted ))/test...
Applying 9 Relocation(s) with Addend for (( omitted ))/test bindnow=0, deepbind=0
Applying 3 PLT Relocation(s) with Addend for (( omitted ))/test bindnow=0, deepbind=0
PLT Resolver injected in plt.got at 0x100003ff8
Calling Init for /lib/libcrypto.so.3 @0x3f0104c000
394474|0x3f011654eb: Calling getenv("OPENSSL_ia32cap") => return 0x0(nil)
Run X86 (0x36ecab30), RIP=0x3f011655aa, Stack=0x765a8bffe1d0 is32bits=0
Done Init for /lib/libcrypto.so.3
Calling Init[0] for /lib/libcrypto.so.3 @0x4d100
FillBlock triggered a segfault at 0x4d100 from 0x3528682a
FillBlock at 0x4d100 triggered a segfault, canceling
Run X86 (0x36ecab30), RIP=0x4d100, Stack=0x765a8bffe1f8 is32bits=0
394474|SIGSEGV @0x351b2a96 (???(./test+0x9b2a96)) (x64pc=0x4d100/???:"???", rsp=0x765a8bffe1f8, stack=0x765a8b800000:0x765a8c000000 own=(nil) fp=0x765a8bffe200), for accessing 0x4d100 (code=1/prot=87), db=(nil)((nil):(nil)/(nil):(nil)/???:clean, hash:0/0) handler=(nil)
RSP-0x20:0x0000765a8bffe200 RSP-0x18:0x0000000000000000 RSP-0x10:0x0000003f0104c01b RSP-0x08:0x0000000000000000
RSP+0x00:0x0000000000010080 RSP+0x08:0x0000000000000000 RSP+0x10:0x0000000000000001 RSP+0x18:0x0000765a8bffe6e1
RAX:0x0000000002d82203 RCX:0x0000000000000000 RDX:0x0000765a8bffe220 RBX:0x0000000000000000 
RSP:0x0000765a8bffe1f8 RBP:0x0000765a8bffe200 RSI:0x0000765a8bffe210 RDI:0x0000000000000001 
 R8:0x0000000000000000  R9:0x02d8220300000000 R10:0x0000000047888915 R11:0x000000000000000f 
R12:0x0000000000000000 R13:0x0000000000000000 R14:0x0000000000000000 R15:0x0000000000000000 
ES:0x002b CS:0x0033 SS:0x002b DS:0x002b FS:0x0043 GS:0x0053

Investigation

I investigated the code via gdb, and found that the function RunElfInit in src/elfs/elfloader.c called RunFunctionWithEmu for .init and functions in .init_array:

box64/src/elfs/elfloader.c

Lines 1108 to 1118 in f9b7464

if(h->initentry)
RunFunctionWithEmu(emu, 0, p, 3, my_context->argc, my_context->argv, my_context->envv);
printf_dump(LOG_DEBUG, "Done Init for %s\n", ElfName(h));
// and check init array now
Elf64_Addr *addr = (Elf64_Addr*)(h->initarray + h->delta);
for (size_t i=0; i<h->initarray_sz; ++i) {
if(addr[i]) {
printf_dump(LOG_DEBUG, "Calling Init[%zu] for %s @%p\n", i, ElfName(h), (void*)addr[i]);
RunFunctionWithEmu(emu, 0, (uintptr_t)addr[i], 3, my_context->argc, my_context->argv, my_context->envv);
}
}

When this issue happened, the pointer p is 0x0000003f0104c000 and addr[i] is 0x4d100 (SEEMS TOO LOW???)

I read /proc/(( the pid ))/maps and saw

... (( omitted ))
3f00000000-3f000ca000 r--p 00000000 103:09 39607298                      /usr/lib/libssl.so.3
3f000ca000-3f000da000 rw-p 00000000 00:00 0 
3f01000000-3f01465000 r--p 00000000 103:09 39607296                      /usr/lib/libcrypto.so.3
3f01465000-3f014cc000 rw-p 00000000 00:00 0 
... (( omitted ))

Here is the problem. The pointer p was ok because 0x3f0104c000 - 0x3f01000000 is 0x4c000 and objdump -alDS /usr/libcrypto.so.3 told me:

Disassembly of section .init:

000000000004c000 <.init>:
   4c000:	f3 0f 1e fa          	endbr64
   4c004:	48 83 ec 08          	sub    $0x8,%rsp
   (( omitted ))

However, addr[i] (value = 0x4d100) seemed too low! objdump -alDS /usr/libcrypto.so.3 told me:

Disassembly of section .init_array:

0000000000465b90 <.init_array>:
  465b90:	00 d1                	add    %dl,%cl
  465b92:	04 00                	add    $0x0,%al
  465b94:	00 00                	add    %al,(%rax)
	...

Here was why addr[i] is 0x4d100 (actually a 4 bytes value: 0x0004d100).

Possible Fixes

I think box64 should handle .init_array properly, for example, relocating the entries in .init_array.

By the way, I can help to fix it if this is ok.

It is my first time to fix something for box64, so I would appreciate it if you tell me the proper way to fix this bug and guide me how to contribute for box64. Thanks!

Well, as you have see in the code, INIT_ARRAY should be handled properly.

Currently, the value of the array are just translated by the "delta" (difference between loaded memory and physical elf file offset). and this value is called yes.

Regarding your crash, I'm unclear what box64 should do there? delta+ainit_array[i] is not correct? What should it be then?

The problem comes from

RunFunctionWithEmu(emu, 0, (uintptr_t)addr[i], 3, my_context->argc, my_context->argv, my_context->envv);

The address of .init_array is correctly relocated, but the entries inside it seems not.

I debugged box64 and saw that:

  1. if the ELF was the program (i.e. my x64 applications) itself, the addr[i] was correct enough: the program was loaded to 0x0000000100000000 and the addr[i] here was 0x0000000100001150.
  2. if the ELF was the shared object (i.e. libcrypto.so, libssl.so), the addr[i] was definitely wrong: the shared object was loaded to 0x0000003f01000000 but the addr[i] here was too low (0x000000000004d100).

I found that:

  1. in my x64 app, .rela.dyn section contains some R_X86_64_RELATIVE about .init_array, so the entries in .init_array are relocated correctly.
  2. in my /usr/lib/libcrypto.so.3, .rela.dyn section does not contain any R_X86_64_RELATIVE, so the entries in .init_array are not relocated.
2. in my `/usr/lib/libcrypto.so.3`, `.rela.dyn` section does not contain any `R_X86_64_RELATIVE`, so the entries in `.init_array` are not relocated.

This alone does not really help. Is there any section containing a relocation for the .init_array entries? If so, in which section is it, and what type of relocation is it? (You can probably find some of this using objdump -rR.)

Also, from the USAGE.md:

box64/docs/USAGE.md

Lines 11 to 16 in aeeb9b7

#### BOX64_LOG *
Controls the Verbosity level of the logs
* 0: NONE : No message (except some fatal error). (Default.)
* 1: INFO : Show some minimum log (Example: librairies not found)
* 2: DEBUG : Details a lot of stuff (Example: relocations or functions called).
* 3: DUMP : All DEBUG plus DUMP of all ELF Info.

Since this is an ELF loader issue, you should use BOX64_LOG=3 or BOX64_LOG=DUMP (these two are functionally equivalent), not just 2 (or DEBUG).
There is also an undocumented BOX64_DUMP=1 which only enables the dumping of loader informations (with which you can keep the log level at whatever you want). BOX64_LOG=3 is in fact equivalent to BOX64_LOG=2 BOX64_DUMP=1.

As a side note, on my (x86_64) machine, your test file runs with no issue with my libcrypto. After analyzing, I found out that my version of the library contains no .init_array section, only a .init (which is correctly relocated and executed).
If you want more help for debugging, providing at least which version of the library you have would be useful (along with where/how you downloaded the library, ie. which package manager or url). As it is, I cannot help more with the informations you have given.

Happy to tell you that I have fixed this issue!

I finally found that it was because box64 did not handle .relr.dyn section, and I tried to handle it (referring to glibc ld.so implementation).

I will send a PR soon.