BlackBoard » Design, Programmierung & Entwicklung » Programmieren » C dynamischer Linker ist gesprächig » Hallo Gast [Anmelden|Registrieren]
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | An Freund senden | Thema zu Favoriten hinzufügen
Neues Thema erstellen Antwort erstellen
Zum Ende der Seite springen dynamischer Linker ist gesprächig
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Misel Misel ist männlich
Hüter des Kitkat


images/avatars/avatar-2084.png

Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d e

Fragezeichen dynamischer Linker ist gesprächig       Zum Anfang der Seite springen

Hallo,

ich hab ne Programmieraufgabe gelöst und das Programm läuft so, wie es soll, aber zwischendrin meldet sich der dynamische Linker auf stderr zu Wort, obwohl eigentlich alles in Ordnung sein sollte.

Die Aufgabe selbst besteht darin, zwei Programme zu schreiben, die flip bzw. flop ausgeben und sich mit exec gegenseitig aufrufen, das macht er ja auch ohne Probleme:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
./flip 12 2> /dev/null 
                        11: flop
10: flip
                        9: flop
8: flip
                        7: flop
6: flip
                        5: flop
4: flip
                        3: flop
2: flip
                        1: flop
0: flip


ohne die Umleitung von stderr kommt folgendes raus:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
./flip 12
                        11: flop
10: flip
      6176:     symbol=printf;  lookup in file=./flip
      6176:     symbol=printf;  lookup in file=/lib/libc.so.6
      6176:     symbol=_IO_flockfile;  lookup in file=./flip
      6176:     symbol=_IO_flockfile;  lookup in file=/lib/libc.so.6
      6176:     symbol=strlen;  lookup in file=./flip
      6176:     symbol=strlen;  lookup in file=/lib/libc.so.6



ich habe keine Ahnung, wo diese Fehlermeldungen herkommen, ich kompiliere auch ohne Debugoptionen. verwirrt

Edit: Das Mysterium nimmt zu. Auf 2 Slackwarerechnern (gcc v3.3.5) läuft das Programm mit dem obigen Output.

Auf einer Susi (gcc v3.2) und einem Debian Rechner (gcc v3.3.5) an meiner Uni kommt ein Segmentation fault...




Im Anhang findet ihr den Quellcode. Da die beiden Programme essenziell dieselben sind, habe ich ein template erstellt, mit dem über make die Dateien erstellt werden. Also einfach entpacken und make eingeben, dann habt ihr danach die executables und auch die C-Files.

Dateianhang:
unknown flipflop.tgz (1,40 KB, 2 mal heruntergeladen)


__________________
LAUFT! Ich spiele KILLERSPIELE!

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Misel: 29.04.2005 08:53.

28.04.2005 22:43 Misel ist offline E-Mail an Misel senden Homepage von Misel Beiträge von Misel suchen
Black Star Black Star ist männlich
Der Pate [Admin]


images/avatars/avatar-2158.jpg

Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr

      Zum Anfang der Seite springen

Der Debugger gibt folgendes aus:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
(gdb) run 12
Starting program: /tmp/flipflop/flip 12
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
                        11: flop

Program received signal SIGTRAP, Trace/breakpoint trap.
0x41c85920 in ?? () from /lib/ld-linux.so.2
(gdb) bt
#0  0x41c85920 in ?? () from /lib/ld-linux.so.2

Beim disassemblieren kann man sehen, dass er mit dem char** nicht klar kommt, was auch nicht verwunderlich ist.
Du bist sehr unvorsichtig mit dem Speicher umgegenagen.
char** ist schonmal generell boese - *char[soviel_du_haben_must] ist schon mal besser.
code:
1:
char *c_arguments[3];

Dann muss die Liste c_arguments Null-terminierte-Strings aufweisen, dh
code:
1:
c_arguments[0]="./flop\0";

Das behebt erstmal das Problem.
execv() wusste nicht, wo c_arguments zu ende ist, und hat munter weiter vorwaerts gelsesen.

Ausserdem ist es schlechter Stil, den Speicher fuer c_arguments[x] nicht zu reservieren, aber das scheint hier nicht so tragisch zu sein.
Das Problem war wirklich die fehlende Null-terminierung von c_arguments[0].

__________________
vescere bracis meis

29.04.2005 14:26 Black Star ist offline E-Mail an Black Star senden Homepage von Black Star Beiträge von Black Star suchen
Misel Misel ist männlich
Hüter des Kitkat


images/avatars/avatar-2084.png

Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d e

Themenstarter Thema begonnen von Misel
      Zum Anfang der Seite springen

Zitat:
Original von Black Star
Beim disassemblieren kann man sehen, dass er mit dem char** nicht klar kommt, was auch nicht verwunderlich ist.
Du bist sehr unvorsichtig mit dem Speicher umgegenagen.
char** ist schonmal generell boese - *char[soviel_du_haben_must] ist schon mal besser.
code:
1:
char *c_arguments[3];



Ich weiß, dass es mit [3] besser ist, weil er weiß, wieviel Speicher er braucht, aber was, wenn ich noch einen Parameter hinzufügen möchte im Code? Dann muss ich den Code an zwei unterschiedlichen Stellen bearbeiten -> bad maintainability Augenzwinkern

Zitat:
Original von Black Star
Dann muss die Liste c_arguments Null-terminierte-Strings aufweisen, dh
code:
1:
c_arguments[0]="./flop\0";

Das behebt erstmal das Problem.
execv() wusste nicht, wo c_arguments zu ende ist, und hat munter weiter vorwaerts gelsesen.

Ausserdem ist es schlechter Stil, den Speicher fuer c_arguments[x] nicht zu reservieren, aber das scheint hier nicht so tragisch zu sein.
Das Problem war wirklich die fehlende Null-terminierung von c_arguments[0].

Das ist nicht das Problem.

Ich habe es eben getestet und es hat an den Symptomen nichts geändert. Auf dem Debian-Rechner kommt immer noch "Speicherzugriffsfehler" und auf meinen Slackwarerechnern läuft er durch, aber mit diesen Warnungen.

... muss ich mir doch mal den gdb reinziehen ... :S

Edit: Ich hab noch was getestet: Die unter Slackware kompilierten Binaries produzieren unter Debian den gleichen Fehler und andersrum. Es ist also die glibc, die mit dem Code nicht klarkommt.

__________________
LAUFT! Ich spiele KILLERSPIELE!

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Misel: 29.04.2005 20:44.

29.04.2005 20:33 Misel ist offline E-Mail an Misel senden Homepage von Misel Beiträge von Misel suchen
Black Star Black Star ist männlich
Der Pate [Admin]


images/avatars/avatar-2158.jpg

Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr

      Zum Anfang der Seite springen

Sorry, hab ein paar Bier drinne, aber dein code ist auch generell sche*sse...

du verwendest char* und char**ohne den speicher zu reservieren und du benutzt sprintf, was man auch nicht tuen sollte.
Da ist es kein wunder, wenn seltsame Dinge geschenen.
Ausserdem sind die Funktionen der exec-Familie generell mit Vorsicht zu geniessen.

Wenn dus sauber und dynamisch machen willst, benutzt du snprintf und reservierst mit malloc oder vergleichbarem den Speicher passend vor. Wenn du den Speicher sorgfaeltig verwaltest, sollten sich alle Probleme in wohlgefallen auflösen.

Auf meinem gentoo-system laüft nach den Änderungen alles astrein, alledings muesste noch, um es ganz korrekt zu machen ein malloc bei jedem char* eingebaut werden und snprintf benutzt werden. Ausserdem solltest du checken, ob der execv-call so Problemlos drinne ist (vor allen Dingen multiple calls)


glibc-version: 2.3.4.20041102-r1

gcc: gcc (GCC) 3.3.5-20050130 (Gentoo Linux 3.3.5.20050130-r1, sp-3.3.5.20050130-1, pie-8.7.7.1)

PS:
Zitat:
Dann muss ich den Code an zwei unterschiedlichen Stellen bearbeiten -> bad maintainability Augenzwinkern

Sorry, aber ich reagier aggressiv auf diese dogmatische Lehrweise fuer Programmiersprachen.
Lern Programmieren oder lern Leitsaetze. Entweder du brauchst es wirklich dynamisch, dann machst du es halt ordentlich mit Speicherreservierung und Freigabe, was die Anzahl deiner Code-Zeilen mal locker verdoppelt oder du setzt einfach einen Wert an, der dicke passt und blockst alles, was drueber geht ab. Wozu hat heute jeder min 256MB Ram?
Wenn du weisst, dass du maximal [10] brauchst, nimmst du halt 10. Und da du weisst, dass 3 reichen, nimmst du 3.
Wenn du nochmal was aendern willst, schreib einen Kommetar ueber das execv bzw, das setzen von c_arguments[x], dass man c_arguments auch anpassen muss.
"bad maintainability" -- wtf?
So ist der code auch bad irgendwas.
Bombemsicher und 1000%-flexibel zu programmieren ist enorm umstaendlich und oft auch absolut unangebracht. Du musst halt Kompromisse eingehen. (was die Flexibilitaet angeht - sicher sollte es schon sein).

Und: die Sache mit dem c_..... i_.... s_.... ist seeehr umstritten. Die wenigsten Programmierer sehen diesen Mehraufwand ein, da durch die Verwendung der Variablen der Typ klar ist. Ich persoenlich halte es fuer mehr als ueberfluessig und stehe damit auf der Seite von ~90% der Programmierer. Eine Ausnahme bildet extreme Zeigerarythmetik. Da macht ein p_.... hin und wieder Sinn.

__________________
vescere bracis meis

30.04.2005 03:22 Black Star ist offline E-Mail an Black Star senden Homepage von Black Star Beiträge von Black Star suchen
Misel Misel ist männlich
Hüter des Kitkat


images/avatars/avatar-2084.png

Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d e

Themenstarter Thema begonnen von Misel
      Zum Anfang der Seite springen

okay, zähl erstmal bis 10 und beruhige dich wieder, das sollte nicht zu einem Religionskrieg ausarten.

Dieses Programm ist (leider) zufällig der erste Versuch im Umgang mit der ungarischen Notation gewesen und ich gebe dir da (und den 90% der programmierern) recht, dass es sehr umständlich ist. Man muss es aber wenigstens mal ausprobiert haben.

Ich habe hier vermutlich noch ein Verständnisproblem. Der Speicher wird ja reserviert, entweder indirekt, wie in meinem Beispiel, oder explizit, so wie Du es vorschlägst. Aber wer gibt den dann wieder frei? Das aufgerufene Programm, wenn es beendet?


... back to the drawing board

__________________
LAUFT! Ich spiele KILLERSPIELE!

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Misel: 01.05.2005 19:44.

01.05.2005 19:34 Misel ist offline E-Mail an Misel senden Homepage von Misel Beiträge von Misel suchen
Black Star Black Star ist männlich
Der Pate [Admin]


images/avatars/avatar-2158.jpg

Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr

      Zum Anfang der Seite springen

Sorry, hatte schlechte Laune Augenzwinkern

Schau dir nen c-lehrgang z.B. auf pronix.de an, da wird das sehr gut erklaert.
Es gibt auch ein Ding, das nennt sich c fuer Linux in 21 tagen. Kann man recht locker in 2,1 Stunden durchgehen - ist auch nicht schlecht.

malloc() reserviert dynamisch den Speicher und free() gibt ihn wieder frei, wobei free() fast immer ueberfluessig ist, da der Speicher mit dem Ende des Pointers freigegeben wird. Das macht dann nur Sinn in ner riesen Endlosschleife, wo man Speicher immer mal wieder belegen und freigeben muss.
Oder zur Kontrolle, ob du sauber prgrammiert hast. free() segfaultet fast immer, wenn man Mist gebaut hat.

Sinn mach auch Pointer mit xxx = NULL; zu inititalisieren, damit man logische Fehler in Programmierfehler umwandeln kann.
Einen segfault findet man sehr leicht im Debugger.

Fuer den Debugger mit c-flag -ggdb (nicht beim linken) kompilieren, dann gdb ./datei und dann run <parameter> eingeben. Beim segfault dann bt (wie backtrace) oder where eingeben.
Spart ne Menge Arbeit.

__________________
vescere bracis meis

01.05.2005 20:36 Black Star ist offline E-Mail an Black Star senden Homepage von Black Star Beiträge von Black Star suchen
phlox81 phlox81 ist männlich
Bote des Lichts und Moderator


images/avatars/avatar-2264.jpg

Dabei seit: 19.10.2002
Beiträge: 3.028
Herkunft: Irgendwo im Nirgendwo

      Zum Anfang der Seite springen

Zitat:
Original von Black Star
malloc() reserviert dynamisch den Speicher und free() gibt ihn wieder frei, wobei free() fast immer ueberfluessig ist, da der Speicher mit dem Ende des Pointers freigegeben wird. Das macht dann nur Sinn in ner riesen Endlosschleife, wo man Speicher immer mal wieder belegen und freigeben muss.
Oder zur Kontrolle, ob du sauber prgrammiert hast. free() segfaultet fast immer, wenn man Mist gebaut hat.


Wenn man mit Malloc speicher reserviert, sollte man ihn auch wieder mit free freigeben, da sonst der Speicher bis Programmende belegt ist, auch wenn
der Pointer schon seine Gültigkeit verloren hat.

Devil

__________________
Intelligenz ist eine Illusion des Menschen

phlox81.de | codenode.de
02.05.2005 10:35 phlox81 ist offline E-Mail an phlox81 senden Homepage von phlox81 Beiträge von phlox81 suchen
Misel Misel ist männlich
Hüter des Kitkat


images/avatars/avatar-2084.png

Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d e

Themenstarter Thema begonnen von Misel
      Zum Anfang der Seite springen

da geht's nämlich schon los *g

exec* überschreibt den kompletten Parent-Prozess.

Wenn ich jetzt aber mit malloc() speicher reserviere, dann weiß der neue Prozess davon ja nix mehr, also hab ich irgendwann meinen Speicher voll verwirrt

__________________
LAUFT! Ich spiele KILLERSPIELE!
04.05.2005 21:02 Misel ist offline E-Mail an Misel senden Homepage von Misel Beiträge von Misel suchen
Black Star Black Star ist männlich
Der Pate [Admin]


images/avatars/avatar-2158.jpg

Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr

      Zum Anfang der Seite springen

Zitat:
Aus C und Linux von Martin Graefe
Im Unterschied zu system() wird keine neue Shell gestartet, sondern das auszufuehrende Programm laeuft in dem gleichen Prozess, der die exec-Funktion aufgerufen hat. Bei Beendigung des Programms wird auch der Prozess beendet, d.h. der Prozess kehrt nach erfolgreicher Ausfuerung der exec-Funktion nicht mehr in das urspruengliche Programm zurueck.


D.h. aller reservierter Speicher sollte auch wieder freigegeben werden und alles, was nach exec() steht ist ueberfluessig, ausser einer Fehlerbehandlung, falls exec() fehlgeschlagen ist.

Das Programmierbeispiel dazu sieht so aus:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
#include <stdio.h>
#include <unistd.h>

char *argv[4], *env[2];

argv[0] = "/bin/ls";
argv[1] = "--color";
argv[2] = "/home/martin";
argv[3] = NULL;
env[0] = "LS_COLORS=fi=00:di=01";
env[1] = "NULL";
execve("/bin/ls", argv, env);
perror("execve() failed");


Das Buch ist uebrigens echt gut, wenn dich das Thema wirklich interessiert. Kostet glaubich 24Euro.

Bei deinem speziellen Problem ist das richtig, da du eine Art Endlosschleife von verketteten exec-calls hast.
Das ist theoretisch so eine Art Rekursion, wo nach dem letzten exec() rueckwaerts der Speicher aufgeraeumt wird.
Eine Rekursion kannst du auch nur soweit treiben, bis der Stack voll ist.
Aber das ist ja schon ein krasses Beispiel und irgendwo ein Missbrauch dieser Funktion.

__________________
vescere bracis meis

05.05.2005 23:14 Black Star ist offline E-Mail an Black Star senden Homepage von Black Star Beiträge von Black Star suchen
Baumstruktur | Brettstruktur
Gehe zu:
Neues Thema erstellen Antwort erstellen
BlackBoard » Design, Programmierung & Entwicklung » Programmieren » C dynamischer Linker ist gesprächig

Forensoftware: Burning Board 2.3.6, entwickelt von WoltLab GmbH