How to undo strip - i.e. add symbols back to stripped binary - linux

I have a stripped binary and symbol-file. Is it possible to add the symbols back to binary and create an unstripped binary.
My use-case is using this binary w/ valgrind.

For those tools that do not support separate files for debug information, you can glue the debug sections back to the original binary.
You can do something along these lines, for example:
First build a small program that efficiently extracts an arbitrary chunk from a file
(note that dd will not do this efficiently as we'd have to use bs=1 to support an arbitrary offset and length, and objcopy -O binary does not copy sections that are not ALLOC, LOAD※)
cat <<EOF | gcc -xc -o ./mydd -
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <macros.h>
char buf[1024*1024];
int main(int argc, char** argv) {
char *fin, *fout;
int fdin, fdout;
off_t off;
size_t len;
ssize_t rd;
int status;
if (argc != 5) {
fprintf(stderr, "Usage: %s fin skip count fout\n", argv[0]);
return 1;
}
fin = argv[1];
off = strtoul(argv[2], NULL, 0);
len = strtoul(argv[3], NULL, 0);
fout = argv[4];
fdin = -1;
fdout = -1;
if ((fdin = open(fin, O_RDONLY)) < 0) {
status = errno;
perror(fin);
} else if ((fdout = open(fout, O_WRONLY|O_TRUNC|O_CREAT, 0660)) < 0) {
status = errno;
perror(fout);
} else if (lseek(fdin, off, SEEK_SET) == (off_t)-1) {
status = errno;
perror("Seeking input");
} else {
while (len > 0 && (rd = read(fdin, buf, min(len, sizeof(buf)))) > 0) {
if (write(fdout, buf, rd) != rd) {
/*don't bother with partial writes or EINTR/EAGAIN*/
status = errno;
perror(fin);
break;
}
len -= rd;
}
if (rd < 0) {
status = errno;
perror(fin);
}
}
if (fdin >= 0) close(fdin);
if (fdout >= 0) close(fdout);
return status;
}
EOF
Finally, extract the .debug sections and glue them to the stripped binary.
objcopy `
objdump -h program.dbg |
awk '$2~/^\.debug/' |
while read idx name size vma lma off algn ; do
echo "$name" >&2
echo " --add-section=$name=$name.raw"
./mydd program.dbg 0x$off 0x$size $name".raw"
done
` program program_with_dbg

elfutils comes with the tool eu-unstrip which can be used to merge symbol files with executables. The result can then be used in place of the stripped version.

Valgrind supports separate debug files, so you should use the answer here, and valgrind should work properly with the externalized debug file.

Related

why is copy_file_range() undefined with 4.15 Linux kernel?

The manpage for copy_file_range() at
https://man7.org/linux/man-pages/man2/copy_file_range.2.html
says:
#define _GNU_SOURCE
#include <unistd.h>
ssize_t copy_file_range(
(...)
The copy_file_range() system call first appeared in Linux 4.5
but when I try to compile the user sample code that contains the above, I get
warning: implicit declaration of function ‘copy_file_range’(...)
In function ‘main':
(...)undefined reference to ‘copy_file_range'
and I have Linux 4.15 :
$uname -r
4.15.0-142-generic
NOTE: I don't want to use any user-mode version which may or may not be present in glibc. I want to use the kernel version which should be available according to the manpage and also to all the Googling I could find. I want to know why is it not available - is the documentation completely wrong? Is it missing some crucial requirement for compilation? Am I completely not understanding something basic?
Full data which I think is irrelevant:
Full sample code foobar.c from the documentation:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int fd_in, fd_out;
struct stat stat;
off64_t len, ret;
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(EXIT_FAILURE);
}
fd_in = open(argv[1], O_RDONLY);
if (fd_in == -1) {
perror("open (argv[1])");
exit(EXIT_FAILURE);
}
if (fstat(fd_in, &stat) == -1) {
perror("fstat");
exit(EXIT_FAILURE);
}
len = stat.st_size;
fd_out = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd_out == -1) {
perror("open (argv[2])");
exit(EXIT_FAILURE);
}
do {
ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
if (ret == -1) {
perror("copy_file_range");
exit(EXIT_FAILURE);
}
len -= ret;
} while (len > 0 && ret > 0);
close(fd_in);
close(fd_out);
exit(EXIT_SUCCESS);
}
Full errors:
$ gcc foobar.c
foobar.c: In function ‘main’:
foobar.c:40:19: warning: implicit declaration of function ‘copy_file_range’; did you mean ‘sync_file_range’? [-Wimplicit-function-declaration]
40 | ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
| ^~~~~~~~~~~~~~~
| sync_file_range
/tmp/ccfnzueg.o: In function `main':
foobar.c:(.text+0x127): undefined reference to `copy_file_range'
collect2: error: ld returned 1 exit status

How can I get ALSA library functions to work in Apache CGI?

The following code prints out available ALSA Midi cards; by default there should always be at least one.
However, when accessed through Apache CGI, the snd_card_next() function does not work properly, resulting in the first if statement being activated and no ALSA cards being found.
Why is Apache CGI somehow not able to process this function?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <alsa/asoundlib.h>
#define NL "\r\n"
void get_alsa_midi_cards(void)
{
int card, status, cnt;
int code;
char *card_name;
int i;
card = -1; // use -1 to prime the pump of iterating through card list
status = snd_card_next(&card);
if (status < 0 || card < 0) {
printf ("Content-Type: text/html; charset=utf-8" NL "Cache-Control: no-cache" NL NL);
printf("{\"alsa_midi_card\":[]}");
//printf("Status: %i Card: %i", status, card);
//printf("Status: 404 Not Found" NL NL);
return;
}
printf ("Content-Type: text/html; charset=utf-8" NL "Cache-Control: no-cache" NL NL);
printf("{\"alsa_midi_card\":[");
for (i = 0, cnt = 0; card >= 0; i++) {
if ((code = snd_card_get_longname (card, &card_name)) < 0)
continue;
if (cnt++ > 0)
printf(",");
printf("{\"longname\":\"");
printf(card_name);
printf("\"}");
if ((status = snd_card_next (&card)) < 0)
break;
}
printf("]}");
}
int main (int argc, char *argv[])
{
get_alsa_midi_cards();
return 0;
}
Note: To compile code with gcc, add the -lasound flag.
The Apache user, usually www-data, needs to be added to the 'audio' group in order to access this function.
usermod -a -G audio www-data

How to change directory structure in Linux/Unix from Command Line?

Here is what I want to do: Given Directory "XYZ", I want to be able to setup XYZ in way that once there is new sub-directory ("ABC") created in it, by default that subdirectory contains 3 sub-directories as well ("1","2","3"). Eg: ls -la /ABC/XYZ/ would display 3 folders without me creating those 3 folders manually
use inotify to monitor filesystem events and execute relative operations when capture 'create driectory ABC in XYZ' event. this is a sample from http://onestraw.net/essay/inotify/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/inotify.h>
#define MONITOR_PATH "/var/onestraw/"
#define MONITOR_MASK IN_CREATE | IN_DELETE | IN_ACCESS | IN_MODIFY
inline void _err(const char *str)
{
perror(str);
exit(1);
}
inline void inotify_loop(int fd)
{
char buf[4096];
size_t len;
struct inotify_event *event;
while (1) {
len = read(fd, buf, sizeof(buf));
if (len < 0) {
_err("read() failed");
}
for (event = (struct inotify_event *)buf;
(char *)event < &buf[len];
event =
(struct inotify_event *)((char *)event + sizeof(*event) +
event->len)) {
if (event->mask & IN_CREATE)
printf("add %s\n", event->name);
if (event->mask & IN_DELETE)
printf("delete %s\n", event->name);
if (event->mask & IN_ACCESS)
printf("access %s\n", event->name);
if (event->mask & IN_MODIFY)
printf("modify %s\n", event->name);
}
}
}
int main(int argc, char *argv[])
{
int fd;
if ((fd = inotify_init()) < 0) {
_err("inotify_init() failed");
}
//if (inotify_add_watch(fd, argv[1], MONITOR_MASK) < 0) {
if (inotify_add_watch(fd, MONITOR_PATH, MONITOR_MASK) < 0) {
_err("inotify_add_watch() failed");
}
inotify_loop(fd);
return 0;
}
In order to do that from the command line, install the inotify-tools.
sudo apt-get install inotify-tools
and then you can use the following command to monitor the XYZ directory for create events.
while ret=$(inotifywait -e create /tmp/XYZ); do mkdir /tmp/XYZ/{1,2,3}; done
As soon as any directory or file is created in XYZ, the commands in the while block will execute. mkdir in this case, creating further directories. you can add further checks as per your requirement in the block.

Linux ELF files: Which byte will differ for static and dynamic ELF programs?

I'm working with linux elf files.
I want to detect, if the given elf program is statically linked (full static link, ldd says "not a dynamic executable") or dynamically linked. The ELF is for embedded Linux, so I can't just run it or use ldd utility.
I want to do this entirely in my program, by reading and checking some bytes. I want not to depend on file utility or on libelf, binutils, etc.
Which bytes will be different?
How about using ldd.c from μClibc? It should be fairly easy to strip out any unwanted dependencies / checks if you want. I think this is a smarter approach than trying to figure out all the corner cases from reading man 5 elf, though FWIW it looks to be just checking for a PT_INTERP program header as you suspect in the comments.
Update: There's a few more checks. I've tried to extract the relevant parts, but I can't be sure if I've missed anything so check for yourself. The code checks 32-bit and 64-bit x86 ELF files. It assumes a little-endian architecture.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
int main(int argc, char* argv[])
{
const char* fname = argv[0];
if (argc >= 2) fname = argv[1];
int fd;
struct stat st;
void *mapping;
if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname);
return 1;
}
if (fstat(fd, &st)) {
perror("fstat");
close(fd);
return 1;
}
if ((mapping = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
const Elf32_Ehdr* eh = mapping;
if (st.st_size < (off_t)sizeof(Elf32_Ehdr) ||
eh->e_ident[EI_MAG0] != ELFMAG0 ||
eh->e_ident[EI_MAG1] != ELFMAG1 ||
eh->e_ident[EI_MAG2] != ELFMAG2 ||
eh->e_ident[EI_MAG3] != ELFMAG3 ||
eh->e_ident[EI_VERSION] != EV_CURRENT) {
printf("Not a valid ELF file\n");
return 0;
}
if (eh->e_type != ET_EXEC && eh->e_type != ET_DYN) {
printf("Not executable or shared object\n");
return 0;
}
int is_dynamic = 0;
// change as appropriate, but remember that byteswapping might be needed in some cases
if (eh->e_ident[EI_CLASS] == ELFCLASS32 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_386) {
uint16_t ph_cnt;
for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
const Elf32_Phdr* ph = (const Elf32_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
is_dynamic = 1;
}
}
} else if (eh->e_ident[EI_CLASS] == ELFCLASS64 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_X86_64) {
const Elf64_Ehdr* eh = mapping;
uint16_t ph_cnt;
for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
const Elf64_Phdr* ph = (const Elf64_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
is_dynamic = 1;
}
}
} else {
printf("Unsupported architecture\n");
return 0;
}
munmap(mapping, st.st_size);
close(fd);
printf("%s: %sdynamic\n", fname, is_dynamic?"":"not ");
return 0;
}

Print fifo content and exit

I need to print the content of a fifo (named pipe) to standard output.
I could use the command:
cat fifo
The problem is that cat doesn't return. It stays running, waiting for more content coming from the fifo. But I know there wont be any more content coming for a while so I just want to print what's available.
Is there a command that just print the available content and exit??
EDIT:
In one end of the fifo there is a process writing every now and then the output of different commands. That process is permanently running so there wont be an EOF.
When you can't send an EOF, you could use a 'non-blocking cat'. I've included a (tested) C version i found here (credit goes to the original author over there of course). The magic is in fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK).
The first argument to this non-blocking cat is the number of seconds you want to wait before exiting again.
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
void read_loop(int fFile, double wWait)
{
if (fFile < 0) return;
double max_time = wWait, total_time = 0;
struct timespec cycle_time = { 0, 50 * 1000 * 1000 };
double add_time = (double) cycle_time.tv_sec + (double) cycle_time.tv_nsec / 1000000000.;
char next_line[1024];
FILE *input_file = fdopen(fFile, "r");
while (total_time < max_time)
{
while (fgets(next_line, 1024, input_file))
{
write(STDOUT_FILENO, next_line, strlen(next_line));
total_time = 0;
}
nanosleep(&cycle_time, NULL);
total_time += add_time;
}
fclose(input_file);
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
fprintf(stderr, "%s [max time] (files...)\n", argv[0]);
return 1;
}
int max_wait = strtoul(argv[1],0, 10);
if (argc == 2)
{
fprintf(stderr, "%s: using standard input\n", argv[0]);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
read_loop(STDIN_FILENO, max_wait);
return 0;
}
int current = 2;
while (current < argc)
{
fprintf(stderr, "%s: switch to file '%s'\n", argv[0], argv[current]);
int next_file = open(argv[current++], O_RDONLY | O_NONBLOCK);
read_loop(next_file, max_wait);
close(next_file);
}
return 0;
}
You should close the other end of the FIFO. That should send an EOF to the cat process.

Resources