|
|
|
|
dynamischer Linker ist gesprächig |
Misel
Hüter des Kitkat
Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d
e
|
|
dynamischer Linker ist gesprächig |
|
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.
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.
__________________ 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
Hüter des Kitkat
Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d
e
Themenstarter
|
|
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
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 |
|
|
Black Star
Der Pate [Admin]
Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr
|
|
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
Der Pate [Admin]
Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr
|
|
Sorry, hatte schlechte Laune
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 |
|
|
phlox81
Bote des Lichts und Moderator
Dabei seit: 19.10.2002
Beiträge: 3.028
Herkunft: Irgendwo im Nirgendwo
|
|
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 |
|
|
Misel
Hüter des Kitkat
Dabei seit: 02.11.2002
Beiträge: 1.203
Herkunft: live://home.berlin.d
e
Themenstarter
|
|
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
__________________ LAUFT! Ich spiele KILLERSPIELE!
|
|
04.05.2005 21:02 |
|
|
Black Star
Der Pate [Admin]
Dabei seit: 11.12.2001
Beiträge: 2.282
Herkunft: /dev/stderr
|
|
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 |
|
|
|
|
|
|