SEGFPE error in old ld when trying to run a binary - c

I am trying to compile binaries with heap vulnerabilities to practice binary exploitation. To do so, I need an old version of the libc so that doesn't have all the security checks the modern ones do.
I have on my computer a glibc-2.3.2.so file and a ld-2.3.2.so file. I made a small test program -
7 int main()
8 {
9 char * first, *second;
10 first = malloc(0x40);
11 read(0,first,0x40);
12 free(first);
13 second = malloc(0x40);
14 ((void (*)())second)();
15
16 }
(The only thing not included are #include <stdlib.h> and #include <unistd.h>)
I turned the c file into a .o file with the command
gcc -c firstFit.c -m32
Then, I used g++ for the linking, specifying the old glibc.
g++ firstFit.o -o firstFit -Wl,--rpath=`pwd` -Wl,--dynamic-linker=`pwd`/ld-2.3.2.so -m32
It compiles just fine. However, when I try to run the file there is a floating point exception.
[1] 169605 floating point exception (core dumped) ./firstFit
I ran it under gdb to see where and
→ 0xf7fe9572 div DWORD PTR [ecx+0x164]
Using the gdb backtrace command
#0 0xf7fe9572 in ?? () from /home/zac/programming/cybersec/ctfPrepChalls/heap/ld-2.3.2.so
#1 0xf7ff6abd in ?? () from /home/zac/programming/cybersec/ctfPrepChalls/heap/ld-2.3.2.so
#2 0xf7fe8f13 in ?? () from /home/zac/programming/cybersec/ctfPrepChalls/heap/ld-2.3.2.so
#3 0xf7fe8c27 in ?? () from /home/zac/programming/cybersec/ctfPrepChalls/heap/ld-2.3.2.so
Why would ld be have a floating point error? How would I go about fixing this?
Help is appreciated

Then, I used g++ for the linking, specifying the old glibc.
g++ firstFit.o -o firstFit -Wl,--rpath=pwd -Wl,--dynamic-linker=pwd/ld-2.3.2.so -m32
One of the following two reasons very likely explains your crash:
You do not have complete installation of GLIBC-2.3.2 in your current directory.
In particular, g++ will link against libm.so.6, and if you end up using system libm.so.6 with $(pwd)/libc.so.6, you are very likely to have trouble (mixing bits of GLIBC which came from different builds is a no-no).
You link against system libstdc++.so.6, which has DT_GNU_HASH and no DT_HASH.
GLIBC-2.3.2 is old enough not to support GNU_HASH.
I expect the issue#2 is most likely explanation for SIGFPE.
You can confirm either of these by running
env LD_TRACE_LOADED_OBJECTS=1 ./firstFit
to find loaded objects (do not use ldd, it will lie to you), and looking at the output from:
readelf -d ./firstFit | grep HASH
readelf -d /path/to/libstdc++.so.6 | grep HASH

so, here is the program that only returns 0:
int main( void )
{
return 0;
}
compiling the program
gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled1.c" -o "untitled1.o"
Compilation finished successfully.
linking the program (using gcc, not g++)
gcc -ggdb3 -o "untitled1" "untitled1.o"
Compilation finished successfully.
running the program:
./untitled1
(nothing output)
Suggest: don't mix gcc and g++ for the same program

Related

gcc linking two files not working in macOS

For my compiler course, I'm following the Incremental Approach to Compiler Construction by Abdulaziz Ghuloum with the accompanying tutorial. At some point, we have the following two .c files:
runtime.c
#include <stdio.h>
int main(int argc, char** argv) {
printf("%d\n", entry_point());
return 0;
}
ctest.c
int entry_point() {
return 7;
}
The author then runs the following commands:
$ gcc -Wall ctest.c runtime.c -o test
[bunch of warnings]
$ ./test
7
$
But when I run $ gcc -Wall ctest.c runtime.c -o test I get the following error:
runtime.c:9:20: error: implicit declaration of function 'entry_point' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
printf("%d\n", entry_point());
I want to be able to compile and link my two .c files the same way the author did using gcc, but it keeps throwing me that error. I've been doing some research but the same command ($ gcc file1.c file2.c -o combined) keeps coming up. Help would be appreciated.
I'm running this on MacOS Monterey 12.6 and doing gcc --version displays:
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Thanks in advance
On macOS, the default compiler is clang, not gcc. The later is just a symlink to clang so that's something to keep in mind.
Clang sees the call to entry_point() in runtime.c and doesn't know it yet. Traditional C for such an undefined function is to assume it returns int and does not accept arguments. But Clang goes the safe route by default and instead of just warning about it, treats this as an error since most of the time this assumption is just false and may cause runtime issues.
You have multiple options:
Add a header file that defines int entry_point(void); and #include it in your runtime.c.
Add the line int entry_point(void); near the top of your runtime.c.
Pass -Wno-error=implicit-function-declaration to the compiler.

Program statically linked to newlib failed to run in xv6

I encountered some problems when running newlib-linked programs in xv6. (This is the newlib port I have used)
I used this toolchain to compile the newlib. There aren't any problems in compiling and I do get libc.a, libm.a and other library files.
Then I wrote a hello world program and linked it statically against newlib. The program is simply like this:
#include <stdio.h>
int main()
{ printf("hello world\n");
return 0;
}
But the executable generated is too big for the xv6 filesystem (That's a restriction of design), so I stripped it. After stripping the file size is 53k so it's now ok to put it in fs.
When I run "make qemu" I was able to go into the system, and other programs works fine. But when I run my test program, it stuck for a few second and then it says "panic: loaduvm: addr must be page aligned". Is it because I stripped my program, or there are patches or modifications I have to apply to xv6 source code, or some other reasons?
P.S. I'm using up-to-date version of xv6 from it's offical Github repo, and below is the flags I used to compile my test programs:
cc -fno-pic -static -fno-strict-aliasing -fvar-tracking -fvar-tracking-assignments -static-libgcc -nostartfiles -nostdlib -ffreestanding -nodefaultlibs -fno-builtin -m32 -Wall -MD -gdwarf-2 -fno-omit-frame-pointer -fno-stack-protector -I../include/newlib -o build/_test test.c -L../lib/newlib/ -L../lib/libnosys -e main -Ttext 0 -lc -lm -lnosys
The problem has been solved. I forgot to add a "-N" parameter when linking with GCC.
From my understanding, "-N" and "-Ttext 0" flags both keep the compiled programs aligned in 4k when it is loaded into memory, which is required by xv6.

`bash: ./a.out: No such file or directory` on running executable produced by `ld`

Here is a Hello World code in C:
// a.c
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
I compile it as gcc a.c, which produces a.out as expected and ./a.out prints Hello world... as expected.
Now if I do the compile and link separately:
gcc -c a.c; ld -lc a.o, it run the a.out produced as ./a.out I get the message:
bash: ./a.out: No such file or directory
I Googled that error and it seems that happens when the executable produced is a 32-bit ELF and the machine architecture is 64-bit.
I'm running a 64-bit machine and running file a.out gives:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
Why does this happen?
EDIT:
Output of uname -m
$ uname -m
x86_64
Output of ldd a.out
$ ldd a.out
linux-vdso.so.1 => (0x00007ffeeedfb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa13a7b8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa13abab000)
gcc a.c produces a.out which runs correctly.
ld -lc a.o
There are several things wrong with this command line:
In general, user-level code should never use ld directly, and always use appropriate compiler front end (gcc here) to perform the link.
As you have discovered, the link command line that gcc constructs is quite complicated, and the command line that you've accepted in Joan Esteban's answer is wrong.
If you want to see the actual link command, examine output from gcc -v a.o.
Also note that link command changes significantly when you change gcc command only slightly (e.g. some OSes require different crt1.o depending on whether you are linking multi-threaded executable or not), and the command line is always OS-specific (which is one more reason to never use ld directly).
Libraries should follow object files on command line. So ld -lc a.o is never correct, and should always be (a variant of) ld a.o -lc. Explanation.
Link dynamic executables with gcc foo.o (to use the right paths for CRT and libc, and the dynamic linker / ELF interpreter ld-linux-x86-64.so.2).
Or gcc -nostartfiles foo.o for libc but not CRT _start, if you have a hand-written _start
(For static executables without libc or CRT, you can use ld directly or gcc -nostdlib -static.)
gcc -v foo.o will show you the actual paths GCC used on your system.
The other answers only address how to avoid this1, not the actual question of what happened.
The gcc -c a.c; ld -lc a.o commands you gave produce a pretty obvious warning:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400260
So even if this file could be executed, it will probably crash right away. See #EmployedRussian's answer for an explanation of what you should have done.
The question of why it can't even be executed is still interesting:
$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve(2) returns ENOENT because it can't find the interpreter (which I figured out from file and so on, see below). You'd get the same error from trying to run a file that started with
#!/usr/non-existant-path/bin/bash
As you discovered, the usual reason for this error message is when running an ELF binary on a system without the right dynamic linker and dynamic libraries installed (e.g. a 64bit system without 32bit support installed). In your case, it's because you used a bad link command and made a dynamic executable with a bad interpreter path.
I'm on Ubuntu 15.10, where GNU file version 5.22 reports:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, not stripped
There is no /lib/ld64.so.1 on my system. ldd output is confusing, because ldd uses its default ELF interpreter, not the one specified by the binary.
$ ldd a.out
linux-vdso.so.1 => (0x00007ffc18d2b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e0a79f000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x0000559dbc9d2000)
So it assumes that the runtime interpreter in the binary resolved to the one ldd used itself, I guess.
Your ldd output is probably from an old version too, since it just shows /lib64/ld-linux-x86-64.so.2 for that line. Not taking a bad guess is probably better behaviour, for a weird case like this, but doesn't help you see that your binary has a weird interpreter path.
readelf -l a.out
will decode the ELF headers for you, including the interpreter path. (Thanks to #EmployedRussian's comment for pointing this out.)
Use that:
ld -o a.out -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc c.o /usr/lib/crtn.o

gdb not loading symbols when running standalone shared library

I have a PIC shared library which also has a main function
#include <dtest2.h>
#include <stdio.h>
extern const char elf_interpreter[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
int dtestfunc1(int x,int y) {
int i=0;
int sum = 0;
for(i=0;i<=x;i++) {
sum+=y;
sum+=dtestfunc2(x,y);
}
return sum;
}
int main (int argc, char const* argv[])
{
printf("starting sharedlib main\n");
int val = dtestfunc1(4,5);
printf("val = %d\n",val);
_exit(0);
}
This library is linked to another shared library libdtest2 which has the implementation of dtestfunc2 called from dtestfunc1.
If i run gdb directly on dtestfunc1 using
gdb libdtest1.so
the symbols for libdtest2.so are not loaded by gdb. I cannot step into dtestfunc1 because of this and if i press s, the function just executes and gets out.
If i create a driver program which calls dlopen on the shared library, gdb loads the symbols properly after dlopen is executed and everything works fine.
Why is gdb behaving differently in both these cases?
How can i manually point gdb to the shared library if i am running gdb directly on the shared library?
Note: This is a toy example that mirrors my problem for a much larger shared library. All my binaries and libraries are compiled with the -ggdb3 flag.
Edit: My shared library is runnable. I added the proper interpreter path using the extern definition in the source code. I compiled it with gcc -ggdb3 -O0 -shared -Wl,-soname,libdtest1.so.1 -ldtest2 -L/usr/lib -Wl,-e,main -o libdtest1.so.1.0 dtest1.o.
I can run it and it executes perfectly.Running the shared library is not the issue here.
Why is gdb behaving differently in both these cases?
Because you didn't build libdtest1.so correctly for GDB to work with it.
In particular, your libdtest1.so is lacking DT_DEBUG entry in its dynamic section, which you can confirm like so:
readelf -d libdtest1.so | grep DEBUG
You should see nothing. In a correctly built runnable libdtest1.so (which you can build using -pie flag), the output should look like this:
0x00000015 (DEBUG) 0x0
The runtime loader updates DT_DEBUG to point to its r_debug structure, which then allows GDB to find other loaded shared libraries. Without DT_DEBUG, GDB can't find them.
Update:
After adding the pie flag my build command is gcc -ggdb3 -O0 -pie -shared -Wl,-soname,libdtest1.so.1 -ldtest2 -L/usr/lib -Wl,-e,main -o libdtest1.so.1.0 dtest1.c -I. The DEBUG section is still missing
Terminology: it's not a DEBUG section. It's a DT_DEBUG entry in the .dynamic section.
It is still missing because -shared overrides -pie. Remove -shared from the link line.
You would also not need -Wl,-e,main, nor would you need to specify the .interp -- GCC will do that for you.
The correct link command:
gcc -ggdb3 -O0 -pie -rdynamic dtest1.c -I. -Wl,-soname,libdtest1.so.1 \
-L/usr/lib -ldtest2 -o libdtest1.so.1.0
(Order of sources and libraries on the link line matters, yours is wrong.)
Added bonus: your main will receive correct argc and argv[], instead of bogus values you are getting now.
the easy fix.... recompile the library with (for gcc) the parameter '-ggdb' Then it will have all the symbols available to gdb.

Linking a C program directly with ld fails with undefined reference to `__libc_csu_fini`

I'm trying to compile a C program under Linux. However, out of curiosity, I'm trying to execute some steps by hand: I use:
the gcc frontend to produce assembler code
then run the GNU assembler to get an object file
and then link it with the C runtime to get a working executable.
Now I'm stuck with the linking part.
The program is a very basic "Hello world":
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
I use the following command to produce the assembly code:
gcc hello.c -S -masm=intel
I'm telling gcc to quit after compiling and dump the assembly code with Intel syntax.
Then I use th GNU assembler to produce the object file:
as -o hello.o hello.s
Then I try using ld to produce the final executable:
ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello
But I keep getting the following error message:
/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'
The symbols __libc_csu_fini/init seem to be a part of glibc, but I can't find them anywhere! I tried linking against libc statically (against /usr/lib/libc.a) with the same result.
What could the problem be?
/usr/lib/libc.so is a linker script which tells the linker to pull in the shared library /lib/libc.so.6, and a non-shared portion, /usr/lib/libc_nonshared.a.
__libc_csu_init and __libc_csu_fini come from /usr/lib/libc_nonshared.a. They're not being found because references to symbols in non-shared libraries need to appear before the archive that defines them on the linker line. In your case, /usr/lib/crt1.o (which references them) appears after /usr/lib/libc.so (which pulls them in), so it doesn't work.
Fixing the order on the link line will get you a bit further, but then you'll probably get a new problem, where __libc_csu_init and __libc_csu_fini (which are now found) can't find _init and _fini. In order to call C library functions, you should also link /usr/lib/crti.o (after crt1.o but before the C library) and /usr/lib/crtn.o (after the C library), which contain initialisation and finalisation code.
Adding those should give you a successfully linked executable. It still won't work, because it uses the dynamically linked C library without specifying what the dynamic linker is. You'll need to tell the linker that as well, with something like -dynamic-linker /lib/ld-linux.so.2 (for 32-bit x86 at least; the name of the standard dynamic linker varies across platforms).
If you do all that (essentially as per Rob's answer), you'll get something that works in simple cases. But you may come across further problems with more complex code, as GCC provides some of its own library routines which may be needed if your code uses certain features. These will be buried somewhere deep inside the GCC installation directories...
You can see what gcc is doing by running it with either the -v option (which will show you the commands it invokes as it runs), or the -### option (which just prints the commands it would run, with all of the arguments quotes, but doesn't actually run anything). The output will be confusing unless you know that it usually invokes ld indirectly via one of its own components, collect2 (which is used to glue in C++ constructor calls at the right point).
I found another post which contained a clue: -dynamic-linker /lib/ld-linux.so.2.
Try this:
$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$
Assuming that a normal invocation of gcc -o hello hello.c produces a working build, run this command:
gcc --verbose -o hello hello.c
and gcc will tell you how it's linking things. That should give you a good idea of everything that you might need to account for in your link step.
In Ubuntu 14.04 (GCC 4.8), the minimal linking command is:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
-lc -lgcc -lgcc_s \
hello.o \
/usr/lib/x86_64-linux-gnu/crtn.o
Although they may not be necessary, you should also link to -lgcc and -lgcc_s, since GCC may emit calls to functions present in those libraries for operations which your hardware does not implement natively, e.g. long long int operations on 32-bit. See also: Do I really need libgcc?
I had to add:
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
because the default linker script does not include that directory, and that is where libgcc.a was located.
As mentioned by Michael Burr, you can find the paths with gcc -v. More precisely, you need:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
This is how I fixed it on ubuntu 11.10:
apt-get remove libc-dev
Say yes to remove all the packages but copy the list to reinstall after.
apt-get install libc-dev
If you're running a 64-bit OS, your glibc(-devel) may be broken. By looking at this and this you can find these 3 possible solutions:
add lib64 to LD_LIBRARY_PATH
use lc_noshared
reinstall glibc-devel
Since you are doing the link process by hand, you are forgetting to link the C run time initializer, or whatever it is called.
To not get into the specifics of where and what you should link for you platform, after getting your intel asm file, use gcc to generate (compile and link) your executable.
simply doing gcc hello.c -o hello should work.
Take it:
$ echo 'main(){puts("ok");}' > hello.c
$ gcc -c hello.c -o hello.o
$ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
-dynamic-linker /lib/ld-linux.so.2 -lc
$ ./hello.exe
ok
Path to /usr/lib/crt*.o will when glibc configured with --prefix=/usr

Resources