błąd kompilacji "multiple definition"

błąd kompilacji "multiple definition"
BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
1

Witam.
Mam na imię Mirosław, i jestem po raz pierwszy na tym forum. Chciałbym nieśmiało poprosić o pomoc w rozwiązaniu problemu.
Dodam że nie znam języka C. Zajmuję się linuxem, bashem, php, a kiedyś troszkę liznąłem delphi, ale krótko i dawno temu.
Wracając jednak do sedna sprawy nie mogę skompilować programu, bo ktoś skopał patcha, umieszczając w patchowanym pliku definicję która występuje już w innym.
Więc teraz w dwóch plikach .c występuje konstrukcja: "struct ip_address iplocal;". W pliku nagłówkowym .h includowanym w obu tych plikach jest: "struct ip_address { unsigned char d[4]; } ;"
Przy próbie kompilacji występuje błąd z komunikatem: "/usr/bin/ld: timeoutconn.o:(.bss+0x4): multiple definition of `iplocal'; tcp-env.o:(.bss+0x80): first defined here"
Wiem o co chodzi, ale nie bardzo potrafię to rozwiązać.
Dodam że próbowałem metodą dedukcji różne kombinacje typu extern, itp, lecz bez efektu.
Zlituje się ktoś i udzieli pomocy? Proszę.
Pozdrawiam serdecznie

elwis
  • Rejestracja: dni
  • Ostatnio: dni
3

Bardzo trudno byłoby ten problem rozwiązać nawet nie wiedząc co to za program i bez jakiegokolwiek kodu.

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
2

Skopiuj wlej, logi błędów (jako tekst i nie tylko jedną linikę) i powiązany kod.
Bez tego, to jak pytanie niewidomego o dzieła Picassa.

BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0

Temat ogarnięty, aczkolwiek w/g mnie trochę pokrętnie.
Wczoraj jeszcze raz sprawdziłem opcję z przeniesieniem linijki "struct ip_address iplocal;" do pliku nagłówkowego z dodaniem dyrektywy "extern", ale tym razem zakomentowałem tą linię tylko w jednym pliku.
Zakomentowanie linii tylko w pliku nr. 2 spowodowało komunikat o braku inicjacji, ale zakomentowanie linii tylko w pliku nr. 1 zadziałało, i program się poprawnie skompilował.
W sumie jest OK, ale dziwi mnie tylko to że plik nagłówkowy jest includowany w pliku nr. 1 i w pliku nr. 2, więc wydawało mi się że nie ma potrzeby zostawiać tej linii w drugim pliku.

elwis napisał(a):

Bardzo trudno byłoby ten problem rozwiązać nawet nie wiedząc co to za program i bez jakiegokolwiek kodu.

Program to wiekowy program pocztowy Qmail, profesora Bersteina. Używam go od lat na serwerach firmowych. Już dawno temu przestał się kompilować, więc po prostu kopiowałem binarki skompilowane jeszcze na ubuntu bodajże 16.04. Postanowiłem jednak troszkę go "unowocześnić" m.inn. o obsługę nowszego SSL. Jest sporo łatek, ale jak widać nie koniecznie do końca działają. Co mogłem to poprawiłem, jedynie to mnie zaskoczyło, ale jak już pisałem, temat został ogarnięty. W każdym razie dziękuję za zainteresowanie.

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
1
bluetoth napisał(a):

Temat ogarnięty, aczkolwiek w/g mnie trochę pokrętnie.
Wczoraj jeszcze raz sprawdziłem opcję z przeniesieniem linijki "struct ip_address iplocal;" do pliku nagłówkowego z dodaniem dyrektywy "extern", ale tym razem zakomentowałem tą linię tylko w jednym pliku.

To nie wygląda za dobrze.
Jak na moje oko, poprawiasz coś metodą prób i błędów, bez zrozumienia co właściwie jest problemem.
Całkiem, możliwem że po prostu zamieniłeś jeden problem na inny, który ugryzie cię później.

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1065
0

Ściągnij sobie jakiegoś agenta AI, odpal go w katalogu z projektem i wrzuć komendę kompilacji. To co piszesz brzmi jak jakiś prosty problem tyle, że ciężko wróżyć bez kodu

BR
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 43
2
bluetoth napisał(a):

Program to wiekowy program pocztowy Qmail, profesora Bersteina. (...) Postanowiłem jednak troszkę go "unowocześnić" m.in. o obsługę nowszego SSL. Jest sporo łatek, ale jak widać nie koniecznie do końca działają. Co mogłem to poprawiłem (..)

Dlaczego zamiast go 'unowocześniać' nie zmienić go na już 'unowocześniony'? Zawsze ktoś musi być pierwszy i zawsze ktoś może zrobić to lepiej ale po co się męczyć jak nie trzeba? Szczególnie że 'to nie wygląda za dobrze'. Sprawdzając z ciekawości czy kod jest dostępny znalazłem to:

BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
slsy napisał(a):

Ściągnij sobie jakiegoś agenta AI, odpal go w katalogu z projektem i wrzuć komendę kompilacji. To co piszesz brzmi jak jakiś prosty problem tyle, że ciężko wróżyć bez kodu

No problem jest prosty, i sprowadza się wyłącznie do duplikatu struktury o którym pisałem. Zasadniczo przeniesienie tej struktury do pliku nagłówkowego dostępnego dla obu tych plików, i dodanie dyrektywy "extern" powinno załatwić problem, załatwia, lecz tylko wtedy gdy ta sama struktura pozostaje w pliku nr. 2. I to jest dziwne, albo nie do końca rozumiem logikę tego języka. Co do metody prób i błędów to nie jest tak do końca, gdyż spora część języków programowania posługuje się zbliżoną logiką, a jeśli chodzi o C, to z "grubsza" go rozumiem. Spróbuję jeszcze jak się zachowa kompilator gdy pozostawię te struktury w oryginalnych plikach, dodając extern.

Zamieszczam tylko fragmenty kodu mające związek z błędem kompilatora dla świętego spokoju, ale w/g mnie jest wszystko teraz OK.

plik ip.h (plik ten jest includowany do obu poniższych plików)

Kopiuj
struct ip_address { unsigned char d[4]; } ;

plik timeoutconn.c

Kopiuj
#define IGNORE_BIND_ERROR 0
struct ip_address iplocal;
int bindlocal = 0 ;

plik tcp-env.c

Kopiuj
struct sockaddr_in salocal;
unsigned long localport;
struct ip_address iplocal;

To wywołuje następujący błąd kompilatora:

/usr/bin/ld: timeoutconn.o:(.bss+0x4): multiple definition of `iplocal'; tcp-env.o:(.bss+0x80): first defined here
collect2: error: ld returned 1 exit status

Dodanie "extern" przed "struct ip_address iplocal;" w pliku tcp-env.c rozwiązuje problem. Domyślam się że kompilator dzięki temu nie głupiej z którego pliku struktury użyć. Tak na marginesie starsze kompilatory nie miały z tym problemu :-)

BR
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 43
0

To jest trochę ponad moją głowę. Błąd mnie specjalnie nie dziwi. Czy zmienne nie muszą mieć unikalnych nazw (w swoim zakresie czy zasięgu) ? extern to nie jest coś co można sobie rzucić tak ot.

Sprawdziłem kod programu (mniemam że oryginalny) z jednego z adresów które podałem i o ile mało co rozumiem to zauważyłem znaczącą różnicę. Oczywiście rzeczony kod mógł już wyewoluować. W pliku tcp-env.c jest struct ip_address iplocal; ale w pliku timeoutconn.c jest struct ip_address *ip; a to jest moim zdaniem ogromna różnica. Różnica która tłumaczy brak błędu w starszych kompilatorach.

ip.h
timeoutconn.c
tcp-env.c

BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
brokenelevator napisał(a):

Dlaczego zamiast go 'unowocześniać' nie zmienić go na już 'unowocześniony'?

Tak wiem o stronie sagredo. Ja jednak od lat bazuję na materiałach i łatach ze strony projektu jms. Niestety ale oni porzucili temat dobre parę lat temu.
Jednak jestem przyzwyczajony do tej wersji, i pod tą wersję tworzyłem sporo własnych rozwiązań, a które teraz trudno by było wdrożyć do ich wersji.
W sumie wersja załatana którą ja używam ma wszystko co niezbędne współcześnie. Chodziło mi tylko o skompilowanie na nowo na współczesnej dystrybucji, aby nie przenosić wiecznie binarek. No i stara kompilacja bazowała na starej wersji SSL, więc chciałem nową. Teraz już rozumiesz dlaczego wolę powalczyć z tym co mam? Na podobnej zasadzie wiele firm używa stare programy, czy stare windowsy.

BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
brokenelevator napisał(a):

Sprawdziłem kod programu (mniemam że oryginalny) z jednego z adresów które podałem i o ile mało co rozumiem to zauważyłem znaczącą różnicę. Oczywiście rzeczony kod mógł już wyewoluować. W pliku tcp-env.c jest struct ip_address iplocal; ale w pliku timeoutconn.c jest struct ip_address *ip; a to jest moim zdaniem kolosalna różnica. Różnica która tłumaczy brak błędu w starszych kompilatorach.

Tak, wiem o tym. Dlatego pisałem że ktoś kto pisał tę łatkę skopał temat. Jedyne więc co przyszło mi do głowy, to poinformować kompilator dyrektywą extern.
Sprawdziłem jednak, i ten fragment nadal istnieje w oryginalnym pliku. To jest nowy fragment nowego kodu dodanego przez autora łatki. Nie chcę zaśmiecić forum sporą ilością kodu, więc spróbuję gdzieś umieścić plik oryginalny, i patchowany.

BR
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 43
0
bluetoth napisał(a):

W sumie wersja załatana którą ja używam ma wszystko co niezbędne współcześnie. (...) No i stara kompilacja bazowała na starej wersji SSL, więc chciałem nową. Teraz już rozumiesz dlaczego wolę powalczyć z tym co mam? (...)

Zależnie od tego o jak starej wersji SSL mówimy mógłbym się sprzeczać czy ma wszystko co niezbędne współcześnie.
Czy rozumiem? Trochę tak, trochę nie.

bluetoth napisał(a):

Dlatego pisałem że ktoś kto pisał tę łatkę skopał temat.

Ja też. Nie tylko LLM'y halucynują 😅. Wykreśliłem ten fragment.

bluetoth napisał(a):

Jedyne więc co przyszło mi do głowy, to poinformować kompilator dyrektywą extern.

Myślę że to jest zły pomysł.(sam już nie wiem). Usunięcie komunikatu o błędzie ≠ usunięcie problemu. Jak wyżej wspomniano to może powodować trudne do złapania błędy. Po użyciu extern to będzie jedna i ta sama zmienna. Co jeżeli autor 'skopanej (?)' łatki tak jej nie traktował? Biorąc pod uwagę poniższe, jest szansa że traktował. Wtedy pojawiać się w niej będą nieoczekiwane rzeczy w nieoczekiwanych momentach. Recepta na katastrofę.

Dalsze komentarze sobie podaruję bo jak już pisałem za cienki jestem (i tak pewnie już za dużo napisałem).


Edycja:

Zapytałem LLM'a bo zastanawiało mnie dlaczego działało a teraz nie działa i czy to możliwe. Twierdzi że tak. Jeśli nie halucynuje to historycznie (C89/C90 i przez wiele lat GCC) domyślnie miało tzw. „common symbols”. Czyli jeżeli w kilku plikach było int x; to kompilator emitował symbol typu common, a konsolidator scalał je w jedną wspólną instancję. Od GCC 10 (2020 rok) ta opcja już domyślna nie jest. Czyli wcześniej wiele definicji globalnych bez inicjalizacji było tolerowanych. Teraz są traktowane jako prawdziwe wielokrotne definicje co daje błąd konsolidatora.

Co LLM proponuje?
Jeśli iplocal ma być jedną globalną zmienną współdzieloną przez wiele plików:

W nagłówku:

Kopiuj
extern struct ip_address iplocal { unsigned char d[4]; };

W jednym pliku .c:

Kopiuj
struct ip_address iplocal;
BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
1
brokenelevator napisał(a):

Co LLM proponuje?
Jeśli iplocal ma być jedną globalną zmienną współdzieloną przez wiele plików:

W nagłówku:

Kopiuj
extern struct ip_address iplocal { unsigned char d[4]; };

W jednym pliku .c:

Kopiuj
struct ip_address iplocal;

Z tego co mi wiadomo, a przekopałem się przez sporą ilość stron z historycznymi już informacjami, to o to właśnie autorowi chodziło. To ma być globalna zmienna.
Oczywiście nauczony przezorności przez kilkadziesiąt lat życia, potestuję to wpierw przez dłuższy czas na testowej maszynie wirtualnej, zanim wdrożę na produkcyjnym.
Jeśli coś będzie nie halo wyjdzie w praniu.
W każdym razie dziękuję za poświęcony czas, i życzę rychłej wiosny :-)

KS
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 726
0

Z tego co piszesz problem jest prosty.
Twoim błędem było nie zrobienie zabezpieczenia przed ponownym ładowaniem nagłówka.

Zrozum, #include po prostu kopiuje plik który wskazujesz.

BL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
1
ksh napisał(a):

Z tego co piszesz problem jest prosty.
Twoim błędem było nie zrobienie zabezpieczenia przed ponownym ładowaniem nagłówka.

Zrozum, #include po prostu kopiuje plik który wskazujesz.

Tak wiem jak działą "include" (w php, js, czy bashu efekt działanie jest taki sam). Nie do końca jednak rozumiałem dyrektywę "extern". Wychodziłem z założenia że po prostu "zmusi" kompilator do potraktowanie tego tak jakby występowała tylko jedna zmienna globalna. Zresztą okazało się że tak mniej więcej jest, lecz na początku źle to użyłem. No cóż każdy język ma swoją specyfikę, a ja zajmuję się innymi językami, i dlatego poprosiłem o pomoc na forum, mimo faktu iż problem już rozwiązałem. Wolałem się jednak upewnić że nie popełniłem błędu, a to forum wydało mi się bardzo kompetentne, i się nie pomyliłem. Dziękuję.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.