I wish to inject an object file into an existing binary. The method I am attempting is:
Convert a compiled binary into a relocatable object file.
Use gcc/ld to link the relocatable object file with the object file to be embedded.
Given the source:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
puts("main");
return EXIT_SUCCESS;
}
I compile this to host with the following:
gcc -Wall host.c -o host
I do the conversion to relocatable object file with:
objcopy -B i386 -I binary -O elf64-x86-64 host host.o
I then attempt a link with:
gcc host.o -o host
Ideally, this would relink the relocatable object file back to a binary. This would also give a chance to link in any extra object files. Unfortunately the command gives the following error:
/usr/lib/gcc/x86_64-linux-gnu/4.6.1/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status
My question is why is this error appearing and how would I go about properly relinking?
Something I tried was to link in another object file at this point which contained a dummy main (because I figured I could manually patch up the entry point later anyway), but what happened was that the new binary seemed to relocate the old code in a weird way with the symbol table completely messed up.
Extra Information
readelf on the binary yields the following:
mike#mike-ubuntu:~/Desktop/inject-obj$ readelf -h host
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400410
Start of program headers: 64 (bytes into file)
Start of section headers: 4424 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 27
And on the relocatable object file:
mike#mike-ubuntu:~/Desktop/inject-obj$ readelf -h host.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 8480 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 5
Section header string table index: 2
Rationale
For those interested, the rationale can be found here.
An executable file that is not PIE is impossible to make relocatable. Relocations have already been performed and the record of those relocations was thrown away. That is, relocating it would require finding all addresses of objects or functions inside the code and data of the binary, but it's impossible to determine whether a sequence of bytes is an address or some other sort of data or code.
There should be a way to do what you originally wanted to do (adding in new code), but the approach you're taking is doomed to failure.
Related
I have cross-compiled the paho.mqtt.c to the so file and copy them to my lib directory in the workspace. I use the nm and readelf to check it, nothing goes wrong. But I can't use make to compile the sample client.c
$ readelf -h libpaho-mqtt3c.so
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x2a50
Start of program headers: 52 (bytes into file)
Start of section headers: 168104 (bytes into file)
Flags: 0x5000202, Version5 EABI, soft-float ABI, <unknown>
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 4
Size of section headers: 40 (bytes)
Number of section headers: 31
Section header string table index: 28
$ nm libpaho-mqtt3c.so
00004c44 t MQTTClient_cleanSession
00004acc t MQTTClient_closeSession
000061d0 T MQTTClient_connect
00006234 T MQTTClient_connect5
000062e4 t MQTTClient_connectAll
000058c0 t MQTTClient_connectURI
0000503c t MQTTClient_connectURIVersion
000033b4 T MQTTClient_create
00002db8 T MQTTClient_createWithOptions
000086a0 t MQTTClient_cycle
00003aac t MQTTClient_deliverMessage
00003608 T MQTTClient_destroy
00006be8 T MQTTClient_disconnect
$ make
[ 50%] Linking C executable ../bin/pub
arm-openwrt-linux-uclibcgnueabi-gcc: warning: environment variable 'STAGING_DIR' not defined
arm-openwrt-linux-uclibcgnueabi-gcc: warning: environment variable 'STAGING_DIR' not defined
CMakeFiles/pub.dir/src/pub.c.o: In function `main':
pub.c:(.text+0x78): undefined reference to `MQTTClient_create'
pub.c:(.text+0x9c): undefined reference to `MQTTClient_connect'
pub.c:(.text+0x100): undefined reference to `MQTTClient_publishMessage'
pub.c:(.text+0x140): undefined reference to `MQTTClient_waitForCompletion'
pub.c:(.text+0x168): undefined reference to `MQTTClient_disconnect'
pub.c:(.text+0x174): undefined reference to `MQTTClient_destroy'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/pub.dir/build.make:85: ../bin/pub] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/pub.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
here's my CMakeLists
cmake_minimum_required(VERSION 3.16)
project(CPE LANGUAGES C)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER ${PROJECT_SOURCE_DIR}/toolchain/bin/arm-openwrt-linux-uclibcgnueabi-gcc)
set(CMAKE_CXX_COMPILER ${PROJECT_SOURCE_DIR}/toolchain/bin/arm-openwrt-linux-uclibcgnueabi-g++)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
include_directories(${PROJECT_SOURCE_DIR}/include)
# aux_source_directory(./src SRC_LIST)
set(SRC_LIST ${PROJECT_SOURCE_DIR}/src/pub.c)
find_library(LIB ${PROJECT_SOURCE_DIR}/lib)
add_executable(pub ${SRC_LIST})
target_link_libraries(pub ${LIB})
- find_library(LIB ${PROJECT_SOURCE_DIR}/lib)
+ find_library(LIB paho-mqtt3c ${PROJECT_SOURCE_DIR}/lib)
Given only access to a standalone ELF program I want to be able to call a function within the program from my own program.
Let's say the below code is main.c
#include <stdio.h>
extern int mystery(int a,int b);
int main() {
int a = 0;
int b = 1;
printf("mystery(a,b) = %d\n",mystery(a,b));
return 0;
}
The function mystery exists in some elf file not_my_program.
What I'm trying to do is something along the lines of
gcc main.c not_my_program
However this gives me an undefined reference error to mystery . I've looked for methods
on forums and found that converting this elf file into a shared object file is not possible. I've also looked into compiling main.c into a relocatable object file with
gcc -c main.c
and then using ld to link the elf with main.o but I could not figure out how to do it. The elf is 32 bit but I've omitted the -m32 flag. If the flag is different for ld please let me know. Any help would be very much appreciated.
edit:
output of readelf -h not_my_program
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x10e0
Start of program headers: 52 (bytes into file)
Start of section headers: 15116 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 11
Size of section headers: 40 (bytes)
Number of section headers: 30
Section header string table index: 29
This hacky way worked with a very simple case.
[ aquila ~ ] $ cat 1.c
int func (int a) { return a * (a-1) ; }
int main(int argc) { return func (argc) ; }
[ aquila ~ ] $ cc 1.c
[ aquila ~ ] $ ./a.out ; echo $?
0
[ aquila ~ ] $ readelf -s a.out | grep func
43: 0000000000400487 19 FUNC GLOBAL DEFAULT 11 func
[ aquila ~ ] $ cat 2.c
#include <stdlib.h>
static __attribute__((constructor)) void main() {
int (*func)() = (int (*)())0x0000000000400487;
exit(func(3));
}
[ aquila ~ ] $ cc -fPIC -shared 2.c -o a.so
[ aquila ~ ] $ LD_PRELOAD=./a.so ./a.out ; echo $?
6
The caller in 2.c is made into a constructor with an exit so that the main program's main() is not called, in an attempt to limit the execution of the code other than the caller and func() itself. The return value being 6 instead of 0 shows both that the call worked and that the main program's main() did not get called.
Given only access to a standalone ELF program I want to be able to call a function within the program from my own program
It sounds like you have an XY problem.
While what you desire is technically possible, the difficulty of doing this is approximately 1000x of what you have tried so far. If you are not prepared to spend a month or two getting this working, you should look for other solutions.
Effectively you would have to write a custom ELF loader to load not_my_program into memory and initialize it, but then call mystery instead of main in it.
Note also that mystery may depend on global data, and that data may be initialized in main, so there is no guarantee that mystery will work at all when called before main.
P.S. Would it be sufficient to call mystery from a debugger? That can be achieved in under 30 seconds.
I have already defined _LARGEFILE64_SOURCE and _FILE_OFFSET_BITS 64
to support open() file that more than 2G. That seems to be all right.
But if I try to write() data more than 2G at a time ( such as 64G ), write() will return a value much smaller than 64G (Exactly 2147479552). I guess that write() only can write data smaller than 2G in a time.
Here is my code:
#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <mmap.h>
#define SPACE_SIZE 68719476736
int main()
{
ssize_t err;
void* zeros;
int fd = open64("./test", O_CREAT|O_RDWR|O_LARGEFILE, 0644);
assert(fd != -1);
int zero_fd = open("/dev/zero", O_RDWR);
assert(zero_fd != -1);
zeros = mmap(NULL, SPACE_SIZE, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, zero_fd, 0);
assert(zeros != (void *)-1);
err = write(fd, zeros, SPACE_SIZE);
assert(err == SPACE_SIZE); // Received SIGABRT, err = 2147479552
munmap(zeros, SPACE_SIZE);
}
How to write() data more than 2G in a time?
Supplementary info:
The result of readelf -h ./a.out. a.out is my program's name
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x660
Start of program headers: 64 (bytes into file)
Start of section headers: 6672 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 28
Indeed, and this fact is well documented in man write:
On Linux, write() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred. (This is true on both 32-bit and 64-bit systems.)
So if you're using Linux, there is no way to write 64GB in a single call to write().
You can, however, create a file which appears to contain 64GB of NUL bytes by seeking to offset 64GB-1 and writing a single NUL byte. (Or, as #o11c points out in a comment, you could use ftruncate(fd, SPACE_SIZE);.) Either of those will create a sparse file which will not actually occupy much disk space, but it will act as though it were 64GB of NULs.
I'm trying to understand the behaviour of the GNU linker and how sections are treated.
I'm editing the stm32_flash.ld file in this stm32 project.
When I modify the linker script to put the following as the first section:
.my_test :
{
. = ALIGN(4);
KEEP(*(.my_test))
LONG(0xdeadbeef);
. = ALIGN(4);
} >FLASH
I can see the built binary has the 0xdeadbeef as the first bytes, as I would expect.
$ od -An -tx1 -w1 -v build/program.bin | head
ef
be
ad
de
00
a0
00
20
31
5e
However, if I use the following as the first section:
.my_test :
{
. = ALIGN(8);
KEEP(*(.my_test))
FILL(0xDEADBEEF)
. = 0x8000;
} > FLASH
Then it looks like the linker completely skips this section:
$ od -An -tx1 -w1 -v build/program.bin | head
00
a0
00
20
2d
de
00
08
c1
d9
But I would expect the first 0x8000 bytes to be filled with 0xdeadbeef. Why is the linker ignoring my section?
Does any of your code "emit" to section .my_test?
With your first example, there was a command to "emit" four bytes into that section with the LONG command.
With your second example, there is no explicit "emit" command. FILL only takes effect when it knows how much to fill: nothing emitted, nothing filled.
You either need to tell your code to put some code or data into your .my_test section using __attribute__((__section__(".my_test")) , or simply edit your second example with a LONG again:
.my_test :
{
. = ALIGN(8);
KEEP(*(.my_test))
FILL(0xDEADBEEF)
. = 0x7FFC; /* Note shorter to accommodate following LONG */
LONG(0xBEEFDEAD) /* Deliberately reversed to demonstrate the result */
} > FLASH
I have installed all cross compile packages on my ubuntu system so far but am having a problem and need some help.
Linux 2.6.28.7 #1 CST 2012 armv5tejl unknown
$ cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
BogoMIPS : 199.47
Features : swp half fastmult edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5
Hardware : ServerEngines PILOT3
Revision : 0000
Serial : 0000000000000000
user#ubuntu:~/code$ arm-linux-gnueabi-readelf -h xxx.bin
ELF Header:
Magic: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: ARM
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0xa27c
Start of program headers: 52 (bytes into file)
Start of section headers: 128752 (bytes into file)
Flags: 0x2, GNU EABI, <unknown>
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 6
Size of section headers: 40 (bytes)
Number of section headers: 25
Section header string table index: 24
This is the target machine I need to cross compile for. What flags should I use when compiling?
arm-linux-gnueabi-gcc simple.c -march=armv5 -static -o simplev5
arm-linux-gnueabi-gcc simple.c -mcpu=arm926ej-s -static -o simple926
when I run the simplev5 or simple926, show:
Segmentation fault
follow #Steven P advice, I checked the file format, as follows:
user#ubuntu:~/code$ file simplev5
simplev5: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=f0607da1d809a7d98636d76ee0e538fc828e3b65, not stripped
user#ubuntu:~/code$ file simple926
simple926: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=ed1e6fdade02c0d2c985a503dafb6efadd13522f, not stripped
You most likely have the right compilation or you would get an error about Invalid Format. You can confirm you have the proper file format by using:
file simple926
Try a simpler program:
int main() { return 123; }
Then you can check the result code when you run it to confirm it did something.
./simple926
echo $?
To solve your segmentation fault, you probably need to get out gdb and examine the stack (backtrace).