Dynamically modify symbol table at runtime (in C)
Asked Answered
X

2

9

Is it possible to dynamically modify symbol table at runtime in C (in elf format on Linux)?

My eventual goal is the following:

Inside certain function say foo, I want to override malloc function to my custom handler my_malloc. But outside foo, any malloc should still call to malloc as in glibc.

Note: this is different from LD_PRELOAD which would override malloc during the entire program execution.

Xenocrates answered 10/5, 2015 at 2:16 Comment(0)
S
7

Is it possible to dynamically modify symbol table at runtime in C (in elf format on Linux)?

In theory this is possible, but in practice it's too hard to do.

Inside certain function say foo, I want to override malloc function to my custom handler my_malloc. But outside foo, any malloc should still call to malloc as in glibc.

Modifying symbol table (even if it were possible) would not get you to your desired goal.

All calls from anywhere inside your ELF binary (let's assume foo is in the main executable), resolve to the same PLT import slot malloc@plt. That slot is resolved to glibc malloc on the first call (from anywhere in your program, assuming you are not using LD_BIND_NOW=1 or similar). After that slot has been resolved, any further modification to the symbol table will have no effect.

You didn't say how much control over foo you have.

If you can recompile it, the problem becomes trivial:

#define malloc my_malloc
int foo() {
  // same code as before
}
#undef malloc

If you are handed a precompiled foo.o, you are linking it with my_malloc.o, and you want to redirect all calls from inside foo.o from malloc to my_malloc, that's actually quite simple to do at the object level (i.e. before final link).

All you have to do is go through foo.o relocation records, and change the ones that say "put address of external malloc here" to "put address of external my_malloc here".

If foo.o contains additional functions besides foo, it's quite simple to limit the relocation rewrite to just the relocations inside foo.

Spickandspan answered 10/5, 2015 at 17:38 Comment(3)
> change the ones that say "put address of external malloc here" to "put address of external my_malloc here". Any tool, say gcc, that can do this?Xenocrates
I know it's a very old answer, but we need to change the symbols tables in runtime too, but in our case it is to replace the malloc on dynamically loaded libraries, is there a way to do it?Aseptic
@GuyKorland You should ask a separate question, clearly stating your requirements.Spickandspan
F
0

Is it possible to dynamically modify symbol table at runtime in C (in elf format on Linux)?

Yes, it is not easy, but the functionality can be packaged into a library, so at the end of the day, it can be made practical.

Typemock Isolator++

(https://www.typemock.com/isolatorpp-product-page/isolate-pp/)

This is free-to-use, but closed source solution. The usage example from documentation should be instructive

TEST_F(IsolatorPPTests, IsExpired_YearIs2018_ReturnTrue) {

 Product product;

 // Prepare a future time construct
 tm* fakeTime = new tm();
 fakeTime->tm_year = 2018;

 // Fake the localtime method
 FAKE_GLOBAL(localtime);

 // Replace the returned value when the method is called 
 // with the fake value.
 WHEN_CALLED(localtime(_)).Return(fakeTime);

 ASSERT_TRUE(product.IsExpired());
}

Other libraries of this kind

Alternate approaches

Rewrite code to make it testable

This is easier in other languages than C, but still doable even in C. Structure code into small functions without side-effects that can be unit-tested without resorting to trickery, and so on. I like this blog Modularity. Details. Pick One. about the tradeoffs this brings. Personally, I guturally dislike the "sea of small functions and tons of dependency injection" style of code, but I realize that that's the easiest to work with, all things considered.

Excursion to other languages

What you are asking for is trivial to do in Python, with the unittest.mock.patch, or possibly by just assigning the mock into the original function directly, and undoing that at the end of the test.

In Java, there is Mockito/PowerMock, which can be used to replace static methods for the duration of a test. Static methods in Java approximately correspond to regular functions in C.

In Go, there is Mockey, which works similarly to what needs to be done in C. It has similar limitations in that inlining can break this kind of runtime mocking. I am not sure if in C you can hit the issue that very short methods are unmockable because there is not enough space to inject the redirection code; I think more likely not, if all calls go through the Procedure Linkage Table.

Feticide answered 12/1, 2023 at 9:44 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.