Dynamic Code Optimization/Injection

Project Summary: Dynamic Code Optimization and Injection

The "Dynamic Code Optimization and Injection" project focuses on enhancing the performance of a target program through dynamic code modification and injection techniques. The project involves two main components: the traced process (prog_to_run) and the tracing process (challenges). The traced process contains a function, exponentiation_long_long, targeted for optimization. The tracing process orchestrates various challenges, each demonstrating a unique aspect of dynamic code manipulation. It employs techniques such as interrupting and modifying program execution, allocating memory for optimized code, and utilizing trampolines to seamlessly transition between original and optimized code. The culmination of these challenges results in a significantly accelerated runtime for the traced program. The project showcases advanced system-level programming skills, including low-level code analysis, memory manipulation, and dynamic optimization strategies.

Codename-Plage: Dynamic Code Optimization and Injection

In this project, we have completed challenges 1, 2, 3, and 4.

The source code for the traced process is located in the file src/prog.c:

The source code for the tracing process is in the file src/challenges.c.

The optimized function is in the file src/function_optimized.c.

Compiling the Project

To compile the project, simply run the compil.sh script in the root directory (./compil.sh).

This will compile three programs: two executables, one for tracing and one for the tracer, and one object file for the optimized function.

Note: The executable for prog.c is named prog_to_run.

Running the Project

To run the project, first navigate to the root of the project.

Then, in one terminal, run the first program: ./build/prog_to_run.

In another terminal, run the tracing program with: ./build/challenges prog_to_run. By default, the tracing process launches challenges 3 and 4, but you can add an extra argument to choose challenge 1 or 2 (or 3 for challenges 3 and 4):

Once the tracing program is launched, it performs the operations required in the given challenge:

1) Challenge 1: stops the trace with a trap. 2) Challenge 2: catches the program with a trap, makes it call a function with an argument that is a pointer, catches it afterward, and verifies the results (including the modification of the pointer by the function). Then, it restores the correct execution of the program before pausing. 3) Challenges 3 and 4: - catch the program - allocate space on the HEAP to write the optimized version of the function with posix_memalign - grant execution rights to this allocated memory space with mprotect - write the code of the optimized function at this location - restore any overwritten instructions (in our case, given the size of the function, this is not necessary, but we did it nonetheless to practice good habits) - write a trampoline (jump) to replace the execution of the non-optimized function with the execution of the optimized function just written in memory - resume the process where it was paused, namely the call to this function

You will notice that once the program is launched and finished, the time taken by the traced program through the answer call will be accelerated (by a factor of 10 on our machines).