8 maja 2009

Program, który wie jak bardzo został zoptymalizowany.

Wczoraj na zajęciach z programowania prowadzący zajęcia powiedział nam o bardzo fajnym programie. Otóż taki program, korzystając z błędów kompilatora wypisywał -O1 kiedy był kompilowany z flagą -O1 oraz -O2 kiedy był kompilowany z flagą -O2. Bardzo zainteresowałem się tematem i postanowiłem wyszukać informacje na temat takiego programu w internecie. Niestety nie znalazłem. Na szczęście wyszukałem na tyle potrzebnych informacji, że udało mi się bez trudu napisać program który sprawdza jak był kompilowany:
[npb@localhost ~]$ cat fun.cpp
#include

int main(void)
{
unsigned long l = 0x00110011;
float f = *((float*)(&l));
char* c = (char*)(&f);

if (c[0] == 17) printf("-O1\n");
else printf("-O2\n");

return 0;
}
[npb@localhost ~]$ g++ -O1 -o funO1 fun.cpp
[npb@localhost ~]$ g++ -O2 -o funO2 fun.cpp
[npb@localhost ~]$ ./funO1
-O1
[npb@localhost ~]$ ./funO2
-O2
[npb@localhost ~]$
Program nie korzysta z konkretnego błędu w kompilatorze, tylko z faktu że trochę oszukujemy kompilator. Widać że autor nie do końca sam wiedział co chciał zrobić. Udało mi się rozbudować program w taki sposób aby wykrywał również opcję -O3. Okazało się niestety że nie działa to na wszystkich kompilatorach g++; jednak kod przedstawię:
[npb@localhost ~]$ cat fun.cpp
#include

unsigned long long_it(unsigned long in)
{
return 0x00110011;
}

float float_it(float in)
{
unsigned long v = long_it(*((unsigned long*)(&in)));
return *((float*)(&v));
}

int main(void)
{
float notimportant = 10;
float retval = float_it(notimportant);
char *retvalP = (char*)(&retval);

if (retvalP[0] == 17) printf("-O1\n");
else if (retvalP[0] == 0) printf("-O2\n");
else printf("-O3\n");

return 0;
}
[npb@localhost ~]$ g++ -O1 -o funO1 fun.cpp
[npb@localhost ~]$ g++ -O2 -o funO2 fun.cpp
[npb@localhost ~]$ g++ -O3 -o funO3 fun.cpp
[npb@localhost ~]$ ./funO1
-O1
[npb@localhost ~]$ ./funO2
-O2
[npb@localhost ~]$ ./funO3
-O3
[npb@localhost ~]$

Postawie browara pierwszej osobie, której uda się ten sam trick z opcją -g :)

4 komentarze:

stdin pisze...

Najbardziej lubię piwo marki Żywiec :-)

W GCC 4.4 wprowadzono nowy atrybut: optimize. Korzystając z niego, można stworzyć 4 kopie funkcji: na 3 z nich wymusić optymalizację odpowiednio: O1, O2, O3, a na czwartej nic nie wymuszać, a następnie porównać kod ciał funkcji. Wymaga GCC >= 4.4. Oczywiście działa też z -g.

Kod: tutaj

npb pisze...

Chyba nie wyraziłem zadania wystarczająco jasno. Chodzi o program, który wykryje czy był kompilowany z opcją -g czy nie. Zatem powinien wypisać na ekran coś innego gdy kompilujemy go normalnie, a co innego gdy kompilujemy go z flagą -g. Za kod źródłowy bardzo dziękuję ^^ postaram się go zrozumieć w najbliższy weekend.

stdin pisze...

No to teraz wykrywa -g:

Nowy kod: tutaj.

Można zrobić to samo nie korzystając z readelf, po prostu oglądając kod programu w pamięci i szukając danych w sekcji .debug_str - ale po co pisać własnego readelfa, skoro go mamy :-)

npb pisze...

Rozwiązanie mi się nie podoba :-) No ale trudno - trzebało lepiej precyzować treść zadania - moja wina ;) Nagroda wkrótce ;)