Showing posts with label binutils. Show all posts
Showing posts with label binutils. Show all posts

Thursday, 5 September 2013

PieceOfWonder has not been installed properly.

Faced this message after trying to run this old novel from an old hard drive without reinstalling. The original installer has been lost, what should I do?

First, let's locate the error message in the binary:
$ objdump -s PieceOfWonder.exe
...
5056c0 50696563 654f6657 6f6e6465 72206861  PieceOfWonder ha
5056d0 73206e6f 74206265 656e2069 6e737461  s not been insta
5056e0 6c6c6564 2070726f 7065726c 792e0000  lled properly...
...

Now let's see where the string is referred from:
$ objdump -d PieceOfWonder.exe |grep -C3 5056c0                              
  406ac6:       eb 09                   jmp    0x406ad1                                                            
  406ac8:       6a 00                   push   $0x0                                                                
  406aca:       6a 00                   push   $0x0                                                                
  406acc:       68 c0 56 50 00          push   $0x5056c0                                                           
  406ad1:       e8 21 46 0d 00          call   0x4db0f7                                                            
  406ad6:       8b 4c 24 08             mov    0x8(%esp),%ecx                                                      
  406ada:       33 c0                   xor    %eax,%eax        

Basic block that shows the error message starts from 406ac8, what calls it?

$ objdump -dF PieceOfWonder.exe | grep 4069f2
  4069f2:       0f 84 d0 00 00 00       je     0x406ac8 (File Offset: 0x6ac8)

So the panic routine is launched by the conditional jump at 4069f2.

Let's try replacing it with NOPs (NOP is a 1-byte instruction that does nothing, thus replacing a jump with them will prevent the program from showing the error dialog box and quitting) and see what it does:

WHOA.

In conclusion, in order to fix the problem, grab any hex editor, open the exe file in it, and replace bytes 69f2 through 69f7 with hexadecimal 90.
Example of what you should see before the edit:
000069f0: e800 0f84 d000 0000 480f 84be 0000 0048
And after:
000069f0: e800 9090 9090 9090 480f 84be 0000 0048

PS
Piece of Wonder does not look like the bane of visual novels so far. A lot of the art looks sketchy and rough, but the characters are certainly cute and the whole game had enough charm to make me want to come back to it. Oh, and it also has a great OP song.

PPS
Really surprised I managed to figure out the problem. And Pin was not needed. Still, a short summary on how to obtain an instruction trace of a program in Wine for future reference:
$ cd pin-2.12-58423-gcc.4.4.7-linux/source/tools/ManualExamples && make TARGET=ia32 # make itrace, must be the same arch as the instrumented program
$ winedbg PieceOfWonder.exe
$ .../pin-2.12-58423-gcc.4.4.7-linux/pin.sh -pid $(PoW_exe_pid) -t .../pin-2.12-58423-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/itrace.so
Wine-dbg>c

PPPS
Scratch itrace, DebugTrace *is* the PIN tool you want to use! Proper instruction + memory trace with value resolution, mmmm. To run it, do smth like ".../pin/pin -t .../pin/source/tools/DebugTrace/obj-ia32/debugtrace.dylib -instruction -i -memory -unique_logfile -flush -- ./my_binary"

Saturday, 2 April 2011

No such file or directory after ld.

I was toying with the Linux development tools trying to figure out the whole compilation process of a program (something I should have done a loooong time ago) and ran into this interesting error. Or, rather, quite a boring one, but with a baffling manifestation for a permanent newbie like me.

What I wanted to do was to go through the whole source->compiler->assembler->linker->binary tool invocation chain manually instead of relying on GCC. I made a typical C program:
#include <stdio.h>

main()
{
printf ("Le ha-ha.\n");
}
Ran a typical compiler with the -S option to get a typical assembly source rather than a typical ready-to-go binary:
gcc -S hello.c -o hello.S
Assembled it into a typical ELF object file:
as hello.S -o hello.o
And, finally linked it with libc containing printf() and the crt* wrappers.
ld hello.o /usr/lib/crt* /usr/lib/libc.so -o hello
I say, that was quite simple! Let's run the bastard!
$ ./hello
bash: ./hello: No such file or directory
Huh? I guess there WAS an error, but the stupid tools didn't report it. Let's see which file is missing:
$ ls
hello hello.c hello.o hello.S
Err, what? The binary is present? What about the permissions?
$ ls -lh ./hello
-rwxr-xr-x 1 * * 4.3K 2011-04-03 03:43 hello
WTF?
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
So the file actually EXISTS and it IS executale. However, when I run it, the system says it's absent.

Guess what? Linux actually doesn't find one file when I invoke my program, however, it is not my binary.

Let's dive into the details. If you run GCC with the -v option, it prints all the commands it executes. You can find the linking stage there too, although it's performed through a wrapper called collect2. My line was the following:
"/usr/lib/gcc/i486-linux-gnu/4.4.1/collect2" "--build-id" "--eh-frame-hdr" "-m" "elf_i386" "--hash-style=both" "-dynamic-linker" "/lib/ld-linux.so.2" "-z" "relro" "/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crt1.o" "/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crti.o" "/usr/lib/gcc/i486-linux-gnu/4.4.1/crtbegin.o" "-L/usr/lib/gcc/i486-linux-gnu/4.4.1" "-L/usr/lib/gcc/i486-linux-gnu/4.4.1" "-L/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib" "-L/lib/../lib" "-L/usr/lib/../lib" "-L/usr/lib/gcc/i486-linux-gnu/4.4.1/../../.." "-L/usr/lib/i486-linux-gnu" "/tmp/ccWw7lET.o" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "/usr/lib/gcc/i486-linux-gnu/4.4.1/crtend.o" "/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crtn.o"
After some trial and error I found out that the option I needed was this:
"-dynamic-linker" "/lib/ld-linux.so.2"
It specifies the name of the dynamic linker that will be used on program invocation. But how on earth was I supposed to know that the default dynamic linker wasn't good if the manpage for ld says:
The default dynamic linker is normally correct; don't use this unless you know what you are doing.
Liars. Let's find out what the default linker is:
$ ld hello.o /usr/lib/crt* /usr/lib/libc.so -o hello.without.explicit.dl
$ ./hello.without.explicit.dl
bash: ./hello.without.explicit.dl: No such file or directory
$ ld --dynamic-linker=/lib/ld-linux.so.2 hello.o /usr/lib/crt* /usr/lib/libc.so -o hello.with.explicit.dl
$ ./hello.with.explicit.dl
Le ha-ha.
$ objdump -s hello.without.explicit.dl > hello.without.explicit.dl.objdump
$ objdump -s hello.with.explicit.dl > hello.with.explicit.dl.objdump
$ diff -C1 hello.with.explicit.dl.objdump hello.without.explicit.dl.objdump
*** hello.with.explicit.dl.objdump 2011-04-03 04:14:11.000000000 +0400
--- hello.without.explicit.dl.objdump 2011-04-03 04:14:00.000000000 +0400
***************
*** 1,7 ****

! hello.with.explicit.dl: file format elf32-i386

Contents of section .interp:
! 8048114 2f6c6962 2f6c642d 6c696e75 782e736f /lib/ld-linux.so
! 8048124 2e3200 .2.
Contents of section .note.ABI-tag:
--- 1,7 ----

! hello.without.explicit.dl: file format elf32-i386

Contents of section .interp:
! 8048114 2f757372 2f6c6962 2f6c6962 632e736f /usr/lib/libc.so
! 8048124 2e3100 .1.
Contents of section .note.ABI-tag:
The only difference was the string value in the .interp section (which apparently specifies the path to the dynamic loader). And instead of /usr/lib/libc.so.1 what I needed was /lib/ld-linux.so.2. So... What does libc.so.1 look like?
$ ls /usr/lib/libc.so.1
ls: cannot access /usr/lib/libc.so.1: No such file or directory
There we have it. So the error we saw was the error about a missing dynamic loader, not the binary itself! But how was I supposed to know that from that message without stepping on this rake once? Beats me.

A query to Google shows that /usr/lib/libc.so.1 is used on SCO UnixWare systems, not Linux. Why ld doesn't put the proper linker name on an i386 Ubuntu system and, on top of that, confuses the user by saying not to touch the --dynamic-linker option is another question I can't answer.

Finally, it seems that the older Linux systems used to complain about a "bad ELF interpreter" which was kind of right. I wonder if the modern behaviour can be considered a bug.

Lesson learned? Even robust tools used for many years on a multitude of platforms may try to trick you. Especially robust tools used for many years on a multitude of platforms.