I read this article recently, and I tried overriding the libc printf function with a doing the following for fun :-
Create an executable that uses printf to print this is a test(printer.c)
Create an c file with a custom puts to print muhahaha, this is a test(custom.c)
Create an object file gcc -fPIC -g -c -Wall custom.c
Create an so file gcc -shared -Wl,-soname,libmystuff.so.1 -o libmystuff.so.1.0.1 custom.o
I add the directory which contains the so file into the LD_PRELOAD environment variable. export LD_PRELOAD=$(pwd)
Try running printer
I'd imagine that muhahaha, this is a test would be printed out but it seems like im doing something wrong. Have I got some concept wrong? Or am I just doing something wrong?
[EDIT]
The code snippets involved are :-
// printer.c
int main() {
printf("this is a test");
return 0;
}
// custom.c
void printf(char *t) {
puts("muhahaha, this is a test");
}
You're supposed to name the library in the LD_PRELOAD environment variable, not the directory.
export LD_PRELOAD=/path/to/libmystuff.so.1.0.1
Related
I have several projects in which I use many custom macros. For example,
//prog.c
#include <stdio.h>
#ifdef DBG_LEVEL_1
#define P(d) printf("%s:%d\n", #d, d);
#else
#define P(...)
#endif
#ifdef INLINE
#define INL static inline
#else
#define INL
#endif
INL void func_Sqr(int a)
{
printf("sqr(a):%d\n", a*a);
}
int main()
{
int a = 3;
P(a);
func_Sqr(a);
return 0;
}
I compile this in several ways,
gcc prog.c
gcc -DINLINE prog.c
gcc -DDBG_LEVEL_1 prog.c
gcc -DDBG_LEVEL_1 -DINLINE-DINLINE prog.c
Is there way to set these macros as enabled as default via environment variable?
We can solve this by creating a makefile where these macros are set. But I want to know if there any Linux environment related solution
Is there way to set these macros as enabled as default via environment variable?
Generally, no there is not. Gcc arguments are not affected by environment variables (except maybe DEPENDENCIES_OUTPUT..).
I advise to write a small wrapper function around the command, for example a bash function:
wgcc() {
gcc "${GCCARGS[#]}" "$#"
}
and then doing:
GCCARGS+=(-DA=1 -DB=2)
wgcc something.c
is trivial to do, easy to understand and maintain and communicate with other team members, easy to implement and share. Suprising haisenbugs will be easy to track - wgcc is unique name different then gcc. Still you could overwrite the original command with gcc() { command gcc "${GCCARGS[#]}" "$#"; } or by creating a /usr/local/bin/gcc file, making it a bit more confusing.
But! You can and I woult strongly advise not to do that, because it will be confusing to others and hard to maintain. You can use COMPILER_PATH environment variable to overwrite compiler tools and provide custom options. In steps:
Create a temporary directory
In that directory link all the subprograms of gcc to it's normal prefix, except the tools the behavior you want to modify, like cc1.
Then create cc1 as a script with that will call the original cc1 but will use some environment variable to pass extra arguments.
Then export the path as COMPILER_PATH so gcc will pick it up.
On my archlinux with gcc10.0.1 I did:
mkdir /tmp/temp
cd /tmp/temp
for i in /usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/{cc1plus,collect2,lto-wrapper,lto1}; do ln -vs "$i"; done
printf "%s\n" '#!/bin/sh' '/usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/cc1 $GCCARGS "$#"' > cc1
chmod +x cc1
export COMPILER_PATH=/tmp/temp
After that you can:
export GCCARGS=-DA=1
gcc -xc - <<<'int main() { printf("A=%d\n", A); }'
./a.out
> A=1
There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".
It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?
A solution was linked from one of the comments. Reproducing it here for posterity:
#include <stdio.h>
#include <unistd.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";
extern "C" {
void lib_entry(void)
{
printf("Entry point of the service library\n");
_exit(0);
}
}
Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.
I have two questions:
What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
Are there other alternatives?
It's difficult to guess what prompted such a radical change
Not really: it never worked correctly.
that breaks so much code
That code was broken already in subtle ways. Now you get a clear indication that it will not work.
Are there other alternatives?
Don't do that?
What problem does dlopening an executable solve?
If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.
Update:
at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.
Here is the code:
// foo.c
#include <stdio.h>
__thread int var;
__attribute__((constructor))
static void init()
{
var = 42;
printf("foo.c init: %d %p\n", var, &var);
}
int bar() {
printf("foo.c bar: %d %p\n", var, &var);
return var;
}
int main()
{
printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar: 42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
int (*bar)(void) = dlsym(h1, "bar");
printf("main.c: %d\n", bar());
return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar: 0 0x7fb7305da73c <<< what?
main.c: 0 <<< what?
This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.
Please see this answer:
https://stackoverflow.com/a/68339111/14760867
The argc, argv question is not answered there, but when I found I needed one, I hacked something together to parse /proc/self/cmdline at runtime for pam_cap.so use.
I've got a 'n.c' as main function, and 'o.c' as import function, like below:
$ cat n.c o.c
int f();
int main(){
f();
return 0;
}
#include<stdio.h>
int f(){
printf("hello\n");
return 2;
}
Then scons file like below:
Library('o.c')
Program('n.c',LIBS=['o'])
What I hope here is to compile o.c and generate libo.a(OK), and n.c will use this '.a' to generate final executable. So I specified LIBS=['o'], in hoep that it will specify an archive file to find libo.a library. But:
$ scons -Q
gcc -o n n.o -lo
/usr/bin/ld: cannot find -lo
collect2: error: ld returned 1 exit status
scons: *** [n] Error 1
Actually, scons interpreted my command to be '-lo', which is to find a dynamic shared library.
This is not what I wanted, because during linking, archive is used like object files. Does '-l' work with archive files, and why scons interprets LIBS to use dynamic link shared libraries?
Thanks.
You also need to specify the path where to search for libraries, in this case:
Program('n.c',LIBS=['o'], LIBPATH=['.'])
Please also check chapter 4 "Building and Linking with Libraries" of our UserGuide, which does not only explain how to create and work with Libraries, it further states that your claim from above "SCons interprets LIBS to use dynamic link shared libraries" is plain wrong. Otherwise the object files would end with *.os instead...
Ok, I going to assume this is an easy question. I have a .c file and a Makefile. I'm using Linux 12.10 ubuntu if that matters. I am trying to understand how I write in terminal to get these two files to create an executable, source, and object file in the directory to where these two files are utilizing make. I have nasm installed but not sure if there is something else I need installed. This is currently what I am doing but can't seem to understand the basics behind what I can do in windows but can't seem to get it to work in linux. I have changed the Makefile to except linux.
I know this is probably super easy but I'm pretty new to linux and don't really understand some of the things I think I should be able to figure out pretty easily so I do apologize if this seems to easy.
$ make firstlab.c firstlab
is what I am typing in terminal after I am in the right directory. My
feedback is "
make: Nothing to be done for `homework1.c'.
gcc homework1.c -o homework1
homework1.c: In function ‘main’:
homework1.c:20:5: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
homework1.c:21:5: warning: incompatible implicit declaration of built-in function ‘scanf’ [enabled by default]
"
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char* argv[])
{
int firstNumber = 0;
int secondNumber = 0;
int result = 0;
printf("Enter first value: ");
scanf("%d", &firstNumber);
printf("Enter second value: ");
scanf("%d", &secondNumber);
if(firstNumber >= secondNumber)
result = firstNumber - secondNumber;
else if(secondNumber > firstNumber)
result = secondNumber + firstNumber;
printf("Result: %d\n", result);
system("pause");
return result;
}
Make File:
##########H
PROJECT = Homework1
##################
CC = gcc
# win 32
#RM = del
#linux
RM = rm -f
BIN = $(PROJECT).exe
OBJ = $(PROJECT).o
all: $(BIN)
clean:
${RM} $(OBJ) $(BIN) $(PROJECT).s
$(BIN): $(OBJ)
$(CC) $(OBJ) -o $(PROJECT).exe
$(OBJ): $(PROJECT).s
$(CC) -c $(PROJECT).s -o $(PROJECT).o
$(PROJECT).s: $(PROJECT).c
$(CC) -c $(PROJECT).c -S -masm=intel
Any help is appreciated.
Are you sure you have a makefile? The output you show doesn't seem to line up with that assumption.
make firstlab.c firstlab is a bit weird. You could just replace it with make firstlab and it would have the same results. If you want an object file type, make firstlab.o.
All of that behaviour depends on make's implicit rules. You probably should write a makefile for your project to control the behaviour better. To support creating the assembly file (firstlab.s) you'll have to do that anyway. A rule something like:
%.s : %.c
$(CC) $(CFLAGS) -S -o $# $<
Should do it. You can make similar rules for the executable and the object files. I strongly recommend a quick glance at the GNU Make Manual to get started.
To fix the printf and scanf warnings, add #include <stdio.h> at the top of your program.
I compile this program:
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
With this command:
gcc -c "hello.c" -o hello
And when I try to execute hello, I get
bash: ./hello: Permission denied
Because the permissions are
-rw-r--r-- 1 nathan nathan 856 2010-09-17 23:49 hello
For some reason??
But whatever... after changing the permissions and trying to execute again, I get
bash: ./hello: cannot execute binary file
I'm using gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
What am I doing wrong here? It's gotta be obvious... it's just too late for me to keep using my tired eyes to try and figure out this simple problem....
P.S. I do (sometimes) work on programs more sophisticated than Hello World, but gcc is doing this across the board...
Take the -c out. That's for making object files, not executables.
The -c flag tells it not to link, so you have an object file, not a binary executable.
In fact, if you ran this without the -o flag, you would find that the default output file would be hello.o.
For reference (and giggles), the man entry on the -c flag:
-c Compile or assemble the source files, but do not link. The linking stage simply is not done.
The ultimate output is in the form of an object file for each source file.
By default, the object file name for a source file is made by replacing the suffix .c, .i, .s,
etc., with .o.
Unrecognized input files, not requiring compilation or assembly, are ignored.
Compile with: gcc hello.c -o hello