C/C++

[Megoldva] Kioptimalizálódhat-e a függvényhívás?

Fórumok

Nézzük az alábbit:

#define INTERRUPT_test_and_run(PIE, PIR, MASK, FNC) (((PIE) & (PIR) & (MASK)) ?\
                                                        (FNC, true) : false)

void __interrupt() INTERRUPT_InterruptManager(void) {

    bool ready;

    ready = false;
    ready |= INTERRUPT_test_and_run(PIE3, PIR3, _PIR3_TMR0IF_MASK, tmr0_isr());
    ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1RXIF_MASK, rx_isr());
    ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1TXIF_MASK, tx_isr());
    if (!ready) RESET();                                                        // unhandled interrupt
}

Gondolhatja-e a fordító, hogy a RESET() hívásához szükséges ready változó helyes előállításához szükségtelen a makróban hivatkozott FNC meghívása? Mert ha ezt gondolja, akkor épp az interrupt handlert fogja kispórolni, és sovány vigasz lesz annak a néhány byte-nak a megspórolása. :) Veszélyes-e ebből a szempontból ez a makró?

Szerk.: Nem azt állítom, hogy most rosszul fordítja - még nem próbáltam ki -, hanem az a kérdés, hogy vajon ez egy annyira ügyetlen makró, amitől állandóan ott a fejem felett a pallos, hogy egyszer csak kispórolja a fordító az IT handler hívását, vagy ez így teljesen jó, netán teljesen rossz, vagy picit módosítani kellene rajta?

Megjegyzés:

Annak, aki esetleg érdekesnek gondolja, úgy tűnik, az eredeti makró - itt fentebb - hibás, s a fordító nem érzi szükségesnek a vessző operátor előtti függvény hívását, hiszen a kifejezés anélkül is kiértékelhető. Akkor viszont nem hívódik az interrupt handler.

[MEGOLDVA] Linux waitpid pipe-pal

Fórumok

Ez már kezd az agyamra menni... Hogy kell lekérni Linux alatt a child visszatérési értékét, ha pipe is van? Rohadtul nem úgy működik, ahogy a man page állítja!

Pipe (és WNOHANG) nélkül minden okés:

    ret = 0;
    if(!(pid = fork())) {
        ret = 1;
        printf("child ret %d\n", ret);
        exit(ret);
    } else
    if(pid > 0) {
        waitpid(pid, &ret, 0);
        printf("parent ret %d\n", ret);
    }

Kimenet:

child ret 1
parent ret 1

Na de nekem az kellene, hogy a gyerek futtasson le egy parancsot, ha nem sikerül, akkor írja ki a hibát, és mindezt a szülő pipe-al olvassa, ugyanakkor a visszatérési érték is kéne, hogy sikerült-e a futtatás.

Teljes példa:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
    char output[4096];
    int stdoutpipe[2], l, ret = 0;
    pid_t pid;

    if(pipe(stdoutpipe) < 0) { fprintf(stderr, "unable to create pipe?\n"); return 1; }
    if(!(pid = fork())) {
        close(1);
        close(2);
        close(stdoutpipe[0]);
        dup2(stdoutpipe[1], 1);
        dup2(stdoutpipe[1], 2);
        close(stdoutpipe[1]);
#ifdef NOTOKAY
        ret = system("echosaaa");
#else
        ret = system("echo");
#endif
        if(errno) perror(NULL);
        printf("child ret %d\n", ret);
        exit(ret);
    }
    if(pid > 0) {
        close(stdoutpipe[1]);
        do {
            if((l = read(stdoutpipe[0], output, 4096)) > 0)
                write(1, output, l);
        } while(waitpid(pid, &ret, WNOHANG) != pid);
        close(stdoutpipe[0]);
    } else {
        fprintf(stderr, "unable to fork?\n");
        close(stdoutpipe[0]);
        close(stdoutpipe[1]);
    }
    printf("parent ret %d exit %d\n", ret, WEXITSTATUS(ret));

    (void)argc; (void)argv;
    return 0;
}

Ez konstans 0-át ad vissza, akármi is a gyerek exit() paramétere.

$ gcc aaa.c -o aaa; ./aaa

child ret 0
parent ret 0 exit 0

$ gcc -DNOTOKAY aaa.c -o aaa; ./aaa
sh: line 1: echoaaa: command not found
child ret 32512
parent ret 0 exit 0

Magyarán a waitpid NEM állítja a státuszt, akkor sem, amikor a gyerek pidjével tér vissza, holott a man page szerint kéne neki. Példától függően vagy konstans -1, vagy konstans 0 lesz minden exit() értékre a ret.

WTF? Miért nem adja vissza a gyerek státuszát a watpid, ha WNOHANG opcióval hívódott?

És ha ez normális, akkor hogy a francba kell lekérni a gyerek státuszát, ha olvasni is akarja az ember a kimenetét??? Alaposan átolvastam a man page-eket, nagyon nem így kéne működnie! Rengeteg példát is áttúrtam, de bakker, azok vagy csak a visszatérési értéket kérik le, vagy a kimenetet, de egyik sem mindkettőt!

Ami leírást meg github-ot találtam, ott mindenhol azt írják, így működnie kéne. De mégsem működik! Arról sehol nem szól fáma, hogy a waitpid hibás értéket adna vissza a status paraméterben.

Stackoverflow-val sem kerültem közelebb a megoldáshoz, ezt találtam, ami hasonló (neki is a waitpid()-el van baja, ha WNOHANG meg van adva, mondjuk ő 0-át kap vissza), habár ez epoll-al is meg van spékelve. A megoldási javaslat: "One possible implementation is to repeat waitpid(..., WNOHANG) until it returns the expected PID", na de hát én pont eleve ezt csinálom! Akkor WTF?

Boost lib linkelesi hiba [a peldakod mar fordul]

Fórumok

Sziasztok,

Nem hasznaltam meg a Boost library-t, es igazabol csak egy projektet szeretnek leforditani ami hasznal, de amikor linkelnem ossze az object file-okat, tele van "undefined reference"-szel (a Boost:Log kellene vegulis csak).

Felturtam a Google-t, de nem adott relevans talalatokat.

Gondoltam, megkerdezem a ChatGPT-t:

https://chatgpt.com/share/673b3d2d-6674-8013-aa59-eef5c3692b17

Keszitett is egy forrast, illetve, hogy hogyan kellene forditani / linkelni, csak sajnos ugyanugy nem mukodik. A forditas / linkeles eredmenye:

https://pastebin.com/ah9ZRJ9z

Mar probaltam kulon forditani aztan linkelni, mindenfele kapcsolokat (pl. -std=c++11 / -std=c++14 / -std=c++17, -DBOOST_LOG_DYN_LINK, ...), de nem jutok elorebb.

Debian 12, GCC 12.2, a Boost library-k elvileg fent vannak (a -dev csomagok is termeszetesen).

 

Valaki tudja, hogyan lehetne mukodokepesse tenni?

 

Koszi

 

Edit:

A fenti kod mar lefordul.

g++  boost.o -lpthread -lboost_log -lboost_log_setup -lboost_thread -o boost

Sajnos amit szeretnek, az me'g nem. Felmerult bennem, hogy amiatt nem megy, hogy C es C++ is van benne.

Egyebken amit el szeretnek erni, hogy ebbol:

https://github.com/SiliconLabs/UnifySDK

le tudjam forditani ezt:

https://github.com/SiliconLabs/UnifySDK/blob/main/applications/zpc/appl…

[MEGOLDVA] Segítségkérés algebrai számoláshoz

Fórumok

Adott egy klasszikus Bresenham vonalhúzó. Ez hibaértéket számol az alfa meghatározásához, az indulópontban és a végpontban ez a hiba 0 (tehát az alfa itt 255, egyáltalán nem átlátszó), a két pont között pedig megfelelően számítódik (és abból az alfa az "a" változóba).

Ezt kellene átalakítani úgy, hogy vegyen figyelembe egy induló és érkező hibaértéket az alapján, hogy a koordináták alsó 8 bitje mi (ez a mostani kód csak azt a speciális esetet kezeli, amikor minden alsó 8 bit 0, ezt kellene általánosítani).
Megjegyzés: 256-tal felszorzott koordináták adódnak át (fix pontos aritmetika, alsó 8 bit: 0=0 egész pixelkoorináta, 127=0.5, 255=0.999999).

/* c[4] - szín, RGBA
 * x0,y0 - induló koordináta (alsó 8 bit a törtrész)
 * x1,y1 - vég koordináta (alsó 8 bit a törtrész)
 */
void line(uint8_t c[4], int32_t x0, int32_t y0, int32_t x1, int32_t y1)
{
    int sx, sy, x2, a;
    int dx, dy, err, e2;

    /* most csak levágjuk a törtrészt, hogy az eredeti Bresenhamnak egész pixelkoordinátákat adjunk át.
     * Levágás helyett kéne beleszámolni a törtészt az err, e2 léptetésébe */
    x0 /= 256;
    y0 /= 256;
    x1 /= 256;
    y1 /= 256;

    if(!c[3] || (x0 == x1 && y0 == y1)) return;

    sx = x0 < x1 ? 1 : -1;
    sy = y0 < y1 ? 1 : -1;
    dx = abs(x1-x0);
    dy = abs(y1-y0);

    err = dx*dx+dy*dy;
    e2 = err == 0 ? 1 : 0xffff7fl/sqrt(err);

    dx *= e2; dy *= e2; err = dx-dy;
    while(1) {
        a = err-dx+dy; if(a < 0) a = -a;
        a = 255 - (a >> 16); a = a * c[3] / 255;
        setpixel(x0, y0, c[0], c[1], c[2], a);
        e2 = err; x2 = x0;
        if(2*e2 >= -dx) {
            if(x0 == x1) break;
            if(e2+dy < 0xff0000l) {
                a = 255 - ((e2+dy) >> 16); a = a * c[3] / 255;
                setpixel(x0, y0 + sy, c[0], c[1], c[2], a);
            }
            err -= dy; x0 += sx;
        }
        if(2*e2 <= dy) {
            if(y0 == y1) break;
            if(dx-e2 < 0xff0000l) {
                a = 255 - ((dx-e2) >> 16); a = a * c[3] / 255;
                setpixel(x2 + sx, y0, c[0], c[1], c[2], a);
            }
            err += dx; y0 += sy;
        }
    }
}

Ha van kedve valakinek kibogarászni ezt, azért iszonyat hálás lennék (ha nagyon ráfeküdnék, biztos meg tudnám oldani, de az agyam most egy C fordító belső lelkivilágával van tele és nem akar átállni, folyton félreszámolom).
Egy Nyílt és Szabad Forráskódú projekthez kell, cserébe listáznám a megoldó nevét, mint kontributor és öregbíteném a hírnevét.

Forrásügyileg egyébként itt található, a hívása vonal esetén itt (ez csak felszorozza a koordinátákat 256-al), Bezier görbe esetén meg itt. Ez utóbbi rekurzívan felbontja az ívet több darab összefüggő vonalra, melyek már eshetnek törtpixelre. Viszont amikor ezeket a köztes koordinátákat átadom a vonalhúzónak, akkor kénytelen vagyok eldobni a törtrészt, mert most még nem kezeli azt. Ehhez kellene, hogy a törtrészből kiszámoljam az induló és vég hibaértéket, hogy aztán a Bresenham úgy húzza a vonalat, hogy már eleve anti-aliasolt pixelekkel indítson, ami most csak a ciklus közepén történik.

EDIT: Leegyszerűsítettem a kiírást, és belinkeltem, hogy pontosan mihez kell.

----------------------------------------------------------------------------------------------------------------------------------------------------------
MEGOLDÁS
Köszönöm mindenkinek a segítséget és a próbálkozásokat! Végül is megoldottam úgy, ahogy eredetileg elképzeltem. Jó sok printf kellett hozzá, hogy megtaláljam a számolási hibát, de meglett.

A lépések:
1. nem lett volna szükséges, de végül is megcseréltem a koordinátákat, hogy ne kelljen sx, sy változó meg abs()
2. átalakítottam, hogy a Bresenham fix pontos aritmetikát használjon (összeadás okés, de szorzásnál ugye le kell osztani 256-al)
3. a Descartes távolságot és a hibaértéket is 256-od törtpixelekre határozza így meg, nem egészpixelekre (A..B)
4. a ciklus léptetését átállítottam 256-ra, így továbbra is egész pixelszámú lépést hajt végre
5. a ciklus végét továbbra is egész pixelekre nézem, így a vége hibaérték kezelése automatikus (A..|B|)
6. az induló hibaértéket meg csak simán hozzáadtam az indoló err értékéhez ((A+hiba)..|B|)

void line(uint8_t c[4], int x0, int y0, int x1, int y1)
{
    int64_t dx, dy, err, e, e2;
    int x2, y2, ex, ey, a;

    if(!c[3] || (x0 / 256 == x1 / 256 && y0 / 256 == y1 / 256)) return;

    if(x1 < x0) { a = x0; x0 = x1; x1 = a; }
    if(y1 < y0) { a = y0; y0 = y1; y1 = a; }

    dx = x1 - x0; dy = y1 - y0; a = dx >> 8; e = dy >> 8;
    err = ((dx*dx) >> 8) + ((dy*dy) >> 8);
    e2 = err == 0 ? 1 : 0xffff7f / sqrt(a*a + e*e);
    dx *= e2; dy *= e2; err = (dx + ((x0 & 0xff) << 24)) - (dy - ((y0 & 0xff) << 24));
    while(1) {
        x2 = x0 >> 8; y2 = y0 >> 8;
        a = err - dx + dy; if(a < 0) a = -a;
        setpixel(x2, y2, c[0], c[1], c[2], (255 - (a >> 24)) * c[3] / 255);
        e2 = 2 * err; e = err;
        if(e2 >= -dx) {
            if(x2 == ex) break;
            if(e+dy < 0xff000000)
                setpixel(x2, y2 + 1, c[0], c[1], c[2], (255 - ((e+dy) >> 24)) * c[3] / 255);
            err -= dy; x0 += 256;
        }
        if(e2 <= dy) {
            if(y2 == ey) break;
            if(dx-e < 0xff000000)
                setpixel(x2 + 1, y2, c[0], c[1], c[2], (255 - ((dx-e) >> 24)) * c[3] / 255);
            err += dx; y0 += 256;
        }
    }
}

Így a végeredmény a teljes törtpixeles távolságra számítódik, de eltolással indul és hamarabb érhet véget, pont, ahogy akartam. Egész pixeles koordinátákra pedig pontosan ugyanazokat az értékeket adja, mint az eredeti Bresenham.

A legszebb az egészben, hogy ez az megoldás csak a ciklus előtt tér el lényegesen, a ciklusmagon belül pontosan ugyanazok és ugyanannyi művelet található, így tehát nem lassít rajta semmit (na jó, kicsit optimalizáltam az eredetihez képest a fenti megoldásban, de értitek).

[MEGOLDVA] wine alatt windows c programban az fflush nem megy

Fórumok

Sziasztok!

Kicsit részletesebben a tárgyban leírtak:

Van egy 3D Gamestudio nevű windows-os fejlesztő környezet amit wine-ban futtatok. Ebben készítek egy házikedvenc programot. A környezetben a Lite-C nyelvet lehet használni. Ez a program nyelve.

A programban van egy rész ami log fájlt készít és az fflush funkciót használja arra az esetre, ha elszállna az alkalmazás, de még volna kiírandó sor a log-ba.

A rendszer (wine + 3D GS) jól működött amíg egyszer a system update (volt ott minden mint a búcsúban) után bemondta az unalmast és végtelen ciklusba került/lefagyott. A debug során kiderült, hogy az fflush hívásán akad el.

Ebben szeretnék segítséget kapni, hogyan lehetne felülkeredni a problémán. Próbáltam downgrade-t, meg 64 bites helyett 32 bites prefixet használni, különféle library-ket felrakni. Egyik sem segített, bár lehet nem a megfelelővel próbálkoztam. Az fflush egyébként az msvcrt.dll-ben található, ha ez számít.

Az OS amit használok Manjaro 24.0.4 Wynsdey, Kernel: x86_64 Linux 6.9.9-1-MANJARO.
wine verzióL: 9.12

Előre is köszönet mindenkinek aki segíteni próbál.

C, C++, E?

Fórumok

Debian rendszerben letöltöttem néhány minimális konverziós utility-t, ami nagyon hasznos lenne számomra. A többsége egyszerű C program, és sikeresen le is fordulnak, de van köztük pár ".e" kiterjesztésű. Ezekkel nem tudok mit kezdeni. Próbáltam, utánakeresni, lefordítani gcc, g++ segítségével, de egyelőre sikertelenül.

Van esélyem ezeket a fájlokat lefordítani különösebb varázslat nélkül? Mi ez az E egyáltalán? Ez tényleg valami C++ fejlesztés, mint a D? (D programokat már írtam, de ahhoz külön fordító kellett.)

Tud valaki segíteni, hogy ezt a pár ".e" konvertert le tudjam fordítani linux környezetben?

Minden információt megköszönök a témában!

Egyszerű, multiplatform UI toolkit

Fórumok

Kerestem egy egyszerűen használható UI toolkit-et ANSI C-ben, és a Nuklear-t találtam (a C++ megfelelője az ImGui, de az nem használható ANSI C-ből).

Nem tudom, hányan ismeritek ezeket, de én nagyon felhúztam magam rajtuk, csupa hazugság a Nuklear. Állítólag kicsi (hát rohadtul nem, a 18000 SLoC az nagyon nem kevés), állítólag hatékony (hát rohadtul nem, folyamatosan memóriát allokálgat és minden képkockánál kovertálgatni kell OpenGL esetén), és állítólag könnyen integrálható (hát rohadtul nem, gyakorlatilag minden glue kódot a nulláról magadnak kell megírni). Ja, és ez az immediate-mode konkrétan egy használhatatlan koncepció, multithreading esélytelen vele például. Ja, és még egy: kurvára nem is C89-ben íródott, hazudik a a README-je...

Szóval, hogy rövidre fogjam, csináltam egy UI toolkitet, ami valóban tudja mindazt, amit a Nuklear igér, de képtelen teljesíteni.
SMGUI

- Státusz-Módú GUI (nem immediate-mode és nem is callback-driven, csak hivatkozni kell a már meglévő változóidra és kész)
- egyaránt használható ANSI C és C++ forrásokból is
- Egyetlen, függőségmentes fejlécfájl (plusz opcionális modulok)
- Platform és backend független (a repóban GLFW3, SDL2/3 és X11 illesztő modulok találhatók, ezek tartalmazzák a glue kódot)
- Bármilyen fontot képes kezelni (a repóban PC Screen Font (amit a Linux Console használ) és vektor fontokhoz SSFN modul található)
- Kis kódbázis (tényleg, mindössze kb. 2500 SLoC)
- Kevés memóriát eszik (gyakorlatilag csak ablak átméretezésnél allokál memóriát, képernyő méretűt és ennyi)
- Könnyen integrálható (kb. 5 sornyi kóddal)
- HTML flow-szerű, flexibilis elrendezés
- Bővíthető a kódodból új widgetekkel (példának egy On-Screen Keyboard-os szövegbeviteli mező van a repóban)
- UTF-8 és többnyelvűség támogatás (a nyelv akár menet közben is változtatható)
- teljes UNICODE támogatás (ami a Nuklear font bakerével képtelenség, túl sok az a 0x10FFFF darab glif egyetlen textúrához)
- multithreading támogatás (ami immediate-mode esetén képtelenség)
- természetesen magyar nyelvű doksival: https://bztsrc.gitlab.io/smgui/index.hu.html

A licensze MIT, szóval szabadon beépíthető akár fizetős alkalmazásokba is.

[MEGOLDVA] Milyen rand algoritmust használ a C++ Windows alatt?

Fórumok

Újabb hülye kérdés a részemről :-) Szóval, melyik pszeudo-random algoritmust használja a Windows alatt az MSVC-vel fordított C++ srand() / rand()?

Kicsit nyomoztam, az MS doksiban azt írták, hogy a Mersenne Twister egyik változata, az MT19937, de nem (ez alapján nem ugyanaz, legalábbis mingw-vel). A másik helyen meg azt olvastam, hogy C++11 óta a Park-Miller, de az sem. Sőt, nem is a Lehner LCG. Na de akkor melyik? Nem az, ami a Linux-os glibc beli, az biztos. Egyébként az is kérdés, van különbség Windows alatt a C-beli rand() és a C++-beli között? Mármint látszatra nem ugyanaz az implementációjuk, de ha C-ben és C++-ban is lefordítok egy minimál programot, akkor ugyanaz az eredményük. Létezik, hogy itt még a mingw is bekavarhat? Mármint a a mingw a Windows-os rand()-ot használja egyáltalán, vagy saját implementációja van abban a libstdc++-ban, amit behúz? Nem azonos a Linux-os glibc-vel, az tuti, de abban nem vagyok biztos, hogy az MSVC-fel fordítottal azonos-e egyáltalán.

Ha számít a fordító és a nyelv is, akkor pontosítok: az MSVC C++ fordító által, a "windows.h" fejléc behúzásakor elérhető srand() és rand() algoritmusa kellene. (Itt egyénként az sem világos, hogy ez a "random" fejlécet, avagy a "cstdlib" fejlécet húzza-e be, merthogy mindkettőben van egy rand(), és nem azonosak.)

És hogy miért kéne: a gondom az, hogy egy jelenlegi Windows-only megoldást próbálok cross-platformként reprodukálni, ami egy voxel világot generál. Sajnos az algoritmusa rand()-ot használ, így hiába ugyanaz a seed érték, Windows és Linux alatt tök más az eredmény, ami nem jó. Ezért akarom kicserélni Linux alatt az srand()-ot és rand()-ot pontosan ugyanarra az implementációra, amit a Windows használ, hogy hordozható legyen az eredmény. Remélem érhető.

Szerk: bakker, mekkora mázlim van, véletlenül belenyúltam a a tutiba az egyik stackoverflow oldal eldugott komment szekciójában! Nem a Lehner, nem is a Park-Miller, hanem egy 1958-as LCG variációt használ, konkrétan ezt:

static int random_seed;

void msvc_cpp_srand(int seed) {
    random_seed = seed;
}

int msvc_cpp_rand() {
    random_seed = (random_seed * 214013 + 2531011) & 0xFFFFFFFF;
    return (random_seed >> 16) & 0x7FFF;
}

Azt továbbra sem tudom, hogy a "windows.h" akkor most a cstdlib-et vagy a random-ot használja-e (illetve azt már tudom, hogy a mingw egyiket sem, külön állatfaj), de már nem is fontos, mivel megvan ez a két konkrét függvény :-)

----------------------------------------------------------------------------------------------------------------------
PONTOSÍTÁS

Szükségét látom tisztázni, mert sokakat félrevezetett (elsőre engem is), hogy az eredeti programban nincs véletlenszám generálás, sőt mi több, valódi véletlenszámokkal garantáltan működésképtelen lenne.

Amire valójában szüksége van, az egy megbízható, kiszámítható bitshuffler. Az, hogy az eredeti fejlesztő erre a rand()-ot használta, csak mert az épp akkor aktuális cstdlib-jében az egy bitshufflerként volt/van implementálva, na az a csillámfaszlámaság csimborasszója.

[MEGOLDVA] Újabb Clang bug

Fórumok

Megpróbálom érthetően leírni, hogy mi is a gond, nem triviális.

Szóval, a régebbi Clang fordítók, és a distro-mban évő 17.0.6 is freestanding módban helyesen .text, .data és .bss szekciókat generál.

Azonban a legfrissebb (forrásból telepített) Clang nem, az ilyent eredményez:

readelf -S exfat.o 
There are 9 section headers, starting at offset 0x1968:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .strtab           STRTAB           0000000000000000  00001830
       0000000000000132  0000000000000000           0     0     1
  [ 2] .text             PROGBITS         0000000000000000  00000040
       0000000000000d10  0000000000000000  AX       0     0     16
  [ 3] .rela.text        RELA             0000000000000000  00001110
       0000000000000708  0000000000000018           8     2     8
  [ 4] .note             NOTE             0000000000000000  00000d50
       0000000000000084  0000000000000000   A       0     0     4
  [ 5] .comment          PROGBITS         0000000000000000  00000dd4
       0000000000000020  0000000000000001  MS       0     0     1
  [ 6] .note.GNU-stack   PROGBITS         0000000000000000  00000df4
       0000000000000000  0000000000000000           0     0     1
  [ 7] .llvm_addrsig     LOOS+0xfff4c03   0000000000000000  00001818
       0000000000000018  0000000000000000   E       8     0     1
  [ 8] .symtab           SYMTAB           0000000000000000  00000df8
       0000000000000318  0000000000000018           1     7     8
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)

Amint látható, BSS szekciónak hűlt helye, van viszont helyette LOOS szekció (ami OS-specifikust jelent), és aminek freestanding módban nem is szabadna léteznie, tehát ez egyértelműen Clang fordító bug.

A kérdésem az, hogy próbáltam a doksit bújni, de nem találtam, viszont hátha valaki itt a HUP-on ismer olyan Clang parancssori kapcsolót, esetleg fordítási opciót, amivel a korábbi működés helyreállítható (azaz hogy freestanding módban NE legyenek OS-specifikus szekciók, csak a hagyományos .text, .data és .bss).

Bármi tipp?

MEGOLDÁS: ezer hála lacos-nak, aki megtalálta az egyik kapcsolót! A hiányzó bss-re is találtam megoldást, az -fno-addrsig -fno-common kapcsolók együttesen eltűntetik a LOOS szekciót és visszahozzák a BSS-t, azaz egy-az-egyben visszaállítják a korábbi működést. Azt nem értem, hogy freestanding módban miért nem ezek az alapértelmezettek, de sebaj, megadható, működik!

X11 matatás Xlib nélkül

Fórumok

Érdekes szösszenet, bár hasznát venni sosem fogom, de mégis lekötött, szerintem roppant tanulságos.

https://hereket.com/posts/from-scratch-x11-windowing/

Hogyan nyissunk X11 ablakot és írjuk rá, hogy "Helló világ", majd fogadjunk event-eket, mindössze 200 sornyi C kódból, bármiféle lib vagy header használata nélkül, csakis közvetlen socket írás / olvasással (POSIX headerök és libc azért kell neki, de semmi más).

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>


int32_t GlobalId = 0;
int32_t GlobalIdBase = 0;
int32_t GlobalIdMask = 0;
int32_t GlobalRootWindow = 0;
int32_t GlobalRootVisualId = 0;

int32_t GlobalTextOffsetX = 10;
int32_t GlobalTextOffsetY = 20;

#define READ_BUFFER_SIZE 16*1024

#define RESPONSE_STATE_FAILED 0
#define RESPONSE_STATE_SUCCESS 1
#define RESPONSE_STATE_AUTHENTICATE 2

#define X11_REQUEST_CREATE_WINDOW 1
#define X11_REQUEST_MAP_WINDOW 8
#define X11_REQUEST_IMAGE_TEXT_8 76
#define X11_REQUEST_OPEN_FONT 45
#define X11_REQUEST_CREATE_GC 55


#define X11_EVENT_FLAG_KEY_PRESS 0x00000001
#define X11_EVENT_FLAG_KEY_RELEASE 0x00000002
#define X11_EVENT_FLAG_EXPOSURE 0x8000


#define WINDOWCLASS_COPYFROMPARENT 0
#define WINDOWCLASS_INPUTOUTPUT 1
#define WINDOWCLASS_INPUTONLY 2

#define X11_FLAG_BACKGROUND_PIXEL 0x00000002 
#define X11_FLAG_WIN_EVENT 0x00000800 

#define X11_FLAG_FG 0x00000004
#define X11_FLAG_BG 0x00000008
#define X11_FLAG_FONT 0x00004000
#define X11_FLAG_GC_EXPOSURE 0x00010000

#define PAD(N) ((4 - (N % 4)) % 4)

void VerifyOrDie(int IsSuccess, const char *Message) {
    if(!IsSuccess)  {
        fprintf(stderr, "%s", Message);
        exit(13);
    }
}

void VerifyOrDieWidthErrno(int IsSuccess, const char *Message) {
    if(!IsSuccess)  {
        perror(Message);
        exit(13);
    }
}

void DumpResponseError(int Socket, char* ReadBuffer) {
        uint8_t ReasonLength = ReadBuffer[1];
        uint16_t MajorVersion = *((uint16_t*)&ReadBuffer[2]);
        uint16_t MinorVersion = *((uint16_t*)&ReadBuffer[4]);
        uint16_t AdditionalDataLength = *((uint16_t*)&ReadBuffer[6]); // Length in 4-byte units of "additional data"
        uint8_t *Message = (uint8_t*)&ReadBuffer[8];

        int BytesRead = read(Socket, ReadBuffer + 8, READ_BUFFER_SIZE-8);

        printf("State: %d\n", ReadBuffer[0]);
        printf("MajorVersion: %d\n", MajorVersion);
        printf("MinorVersion: %d\n", MinorVersion);
        printf("AdditionalDataLength: %d\n", AdditionalDataLength);
        printf("Reason: %s\n", Message);
}

void AuthenticateX11() {
    fprintf(stderr, "Current version of the app does not support authentication.\n");
    fprintf(stderr, "Please run 'xhost +local:' in your terminal to disable cookie based authentication\n");
    fprintf(stderr, "and allow local apps to communication with Xorg without it.");
}

int32_t GetNextId() {
    int32_t Result = (GlobalIdMask & GlobalId) | GlobalIdBase;
    GlobalId += 1;
    return Result;
}

void PrintResponseError(char *Data, int32_t Size) {
    char ErrorCode = Data[1];
    const char *ErrorNames[] = {
        "Unknown Error",
        "Request",
        "Value",
        "Window",
        "Pixmap",
        "Atom",
        "Cursor",
        "Font",
        "Match",
        "Drawable",
        "Access",
        "Alloc",
        "Colormap",
        "GContext",
        "IDChoice",
        "Name",
        "Length",
        "Implementation",
    };

    const char* ErrorName = "Unknown error";
    if(ErrorCode < sizeof(ErrorNames) / sizeof(ErrorNames[0])) {
        ErrorName = ErrorNames[ErrorCode];
    }

    
    uint16_t Minor = *((uint16_t*)&Data[8]);
    uint8_t Major = *((uint8_t*)&Data[10]);

    printf("\033[0;31m");
    printf("Response Error: [%d] %s", ErrorCode, ErrorName);
    printf("	Minor: %d, Major: %d", Minor, Major);
    printf("\033[0m\n");


}

void PrintAndProcessEvent(char *Data, int32_t Size) {
    char EventCode = Data[0];
    const char* EventNames[] = {
        "-- Wrong Event Code --",
        "-- Wrong Event Code --",
        "KeyPress",
        "KeyRelease",
        "ButtonPress",
        "ButtonRelease",
        "MotionNotify",
        "EnterNotify",
        "LeaveNotify",
        "FocusIn",
        "FocusOut",
        "KeymapNotify",
        "Expose",
        "GraphicsExposure",
        "NoExposure",
        "VisibilityNotify",
        "CreateNotify",
        "DestroyNotify",
        "UnmapNotify",
        "MapNotify",
        "MapRequest",
        "ReparentNotify",
        "ConfigureNotify",
        "ConfigureRequest",
        "GravityNotify",
        "ResizeRequest",
        "CirculateNotify",
        "CirculateRequest",
        "PropertyNotify",
        "SelectionClear",
        "SelectionRequest",
        "SelectionNotify",
        "ColormapNotify",
        "ClientMessage",
        "MappingNotify",
    };

#define REPLY_EVENT_CODE_KEY_PRESS 2
#define REPLY_EVENT_CODE_EXPOSE 12

const char* TERMINAL_TEXT_COLOR_RED = "\033[0;32m";
const char* TERMINAL_TEXT_COLOR_CLEAR = "\033[0m";

    if(EventCode == REPLY_EVENT_CODE_EXPOSE) {
        // NOTE: Exposure event
        const char *EventName = "Expose";
        uint16_t SequenceNumber = *((uint16_t*)&Data[2]);
        uint32_t Window = *((uint32_t*)&Data[4]);
        uint16_t X = *((uint16_t*)&Data[8]);
        uint16_t Y = *((uint16_t*)&Data[10]);
        uint16_t Width = *((uint16_t*)&Data[12]);
        uint16_t Height = *((uint16_t*)&Data[14]);
        uint16_t Count = *((uint16_t*)&Data[16]);

        printf(TERMINAL_TEXT_COLOR_RED);
            printf("%s: ", EventName);
        printf(TERMINAL_TEXT_COLOR_CLEAR);

        printf("Seq %d, ", SequenceNumber);
        printf("Win %d: ", Window);
        printf("X %d: ", X);
        printf("Y %d: ", Y);
        printf("Width %d: ", Width);
        printf("Height %d: ", Height);
        printf("Count %d: ", Count);
        printf("\n");
        /* printf("%s: Seq %d\n", EventName, SequenceNumber); */
    } else if(EventCode == REPLY_EVENT_CODE_KEY_PRESS) {
        const char *EventName = "KeyPress";
        char KeyCode = Data[1];
        uint16_t SequenceNumber = *((uint16_t*)&Data[2]);
        uint32_t TimeStamp = *((uint32_t*)&Data[4]);
        uint32_t RootWindow = *((uint32_t*)&Data[8]);
        uint32_t EventWindow = *((uint32_t*)&Data[12]);
        uint32_t ChildWindow = *((uint32_t*)&Data[16]); // NOTE: Always 0
        int16_t RootX = *((int16_t*)&Data[20]);
        int16_t RootY = *((int16_t*)&Data[22]);
        int16_t EventX = *((int16_t*)&Data[24]);
        int16_t EventY = *((int16_t*)&Data[26]);
        int16_t SetOfKeyButMask = *((int16_t*)&Data[28]);
        int8_t IsSameScreen = *((int8_t*)&Data[30]);

        printf(TERMINAL_TEXT_COLOR_RED);
            printf("%s: ", EventName);
        printf(TERMINAL_TEXT_COLOR_CLEAR);

        // NOTE: Temporary hack that will not work everywhere
        int StepSize = 10;
        if(KeyCode == 25) { GlobalTextOffsetY += StepSize; }
        if(KeyCode == 39) { GlobalTextOffsetY -= StepSize; }
        if(KeyCode == 38) { GlobalTextOffsetX -= StepSize; }
        if(KeyCode == 40) { GlobalTextOffsetX += StepSize; }

        printf("Code %u, ", (uint8_t)KeyCode);
        printf("Seq %d, ", SequenceNumber);
        printf("Time %d, ", TimeStamp);
        printf("Root %d, ", RootWindow);
        printf("EventW %d, ", EventWindow);
        printf("Child %d, ", ChildWindow);
        printf("RX %d, ", RootX);
        printf("RY %d, ", RootY);
        printf("EX %d, ", EventX);
        printf("EY %d, ", EventY);
        printf("\n");
    } else {
        const char* EventName = " - Unknown Event Code -";
        if(EventCode < sizeof(EventNames) / sizeof(EventNames[0])) {
            EventName = EventNames[EventCode];
        }
        // printf("-------------Event: %s\n", EventName);
        // for(int i = 0; i < Size; i++) {
            // printf("%c", Data[i]);
        // }
        // printf("\n");
    }

}

void GetAndProcessReply(int Socket) {
    char Buffer[1024] = {};
    int32_t BytesRead = read(Socket, Buffer, 1024);

    uint8_t Code = Buffer[0];

    if(Code == 0) {
        PrintResponseError(Buffer, BytesRead);
    } else if (Code == 1) {
        printf("---------------- Unexpected reply\n");
    } else {
        // NOTE: Event?
        PrintAndProcessEvent(Buffer, BytesRead);
    }
}

int X_InitiateConnection(int Socket) { 
    // TODO: Remove global variables and put them into 'connection' struct.
    int SetupStatus = 1;
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    uint8_t InitializationRequest[12] = {};
    InitializationRequest[0] = 'l';
    InitializationRequest[1] = 0;
    InitializationRequest[2] = 11;

    int BytesWritten = write(Socket, (char*)&InitializationRequest, sizeof(InitializationRequest));
    VerifyOrDie(BytesWritten == sizeof(InitializationRequest), "Wrong amount of bytes written during initialization");

    int BytesRead = read(Socket, ReadBuffer, 8);

    if(ReadBuffer[0] == RESPONSE_STATE_FAILED) {
        DumpResponseError(Socket, ReadBuffer);
    }
    else if(ReadBuffer[0] == RESPONSE_STATE_AUTHENTICATE) {
        AuthenticateX11();
    }
    else if(ReadBuffer[0] == RESPONSE_STATE_SUCCESS) {
        printf("INIT Response SUCCESS. BytesRead: %d\n", BytesRead);

        BytesRead = read(Socket, ReadBuffer + 8, READ_BUFFER_SIZE-8);
        printf("---------------------------%d\n", BytesRead);

        /* -------------------------------------------------------------------------------- */
        uint8_t _Unused = ReadBuffer[1];
        uint16_t MajorVersion = *((uint16_t*)&ReadBuffer[2]);
        uint16_t MinorVersion = *((uint16_t*)&ReadBuffer[4]);
        uint16_t AdditionalDataLength = *((uint16_t*)&ReadBuffer[6]); // Length in 4-byte units of "additional data"

        uint32_t ResourceIdBase = *((uint32_t*)&ReadBuffer[12]);
        uint32_t ResourceIdMask = *((uint32_t*)&ReadBuffer[16]);
        uint16_t LengthOfVendor = *((uint16_t*)&ReadBuffer[24]);
        uint8_t NumberOfFormants = *((uint16_t*)&ReadBuffer[29]);
        uint8_t *Vendor = (uint8_t *)&ReadBuffer[40];

        int32_t VendorPad = PAD(LengthOfVendor);
        int32_t FormatByteLength = 8 * NumberOfFormants;
        int32_t ScreensStartOffset = 40 + LengthOfVendor + VendorPad + FormatByteLength;

        uint32_t RootWindow = *((uint32_t*)&ReadBuffer[ScreensStartOffset]);
        uint32_t RootVisualId = *((uint32_t*)&ReadBuffer[ScreensStartOffset + 32]);

        GlobalIdBase = ResourceIdBase;
        GlobalIdMask = ResourceIdMask;
        GlobalRootWindow = RootWindow;
        GlobalRootVisualId = RootVisualId;

        SetupStatus = 0;
    }

    return SetupStatus;
}

int X_CreatWindow(int Socket, int X, int Y, int Width, int Height) {
    // TODO: Put this into 'connection' struct
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    int32_t WindowId = GetNextId();
    int32_t Depth = 0;
    uint32_t BorderWidth = 1;
    int32_t CreateWindowFlagCount = 2;
    int RequestLength = 8+CreateWindowFlagCount;

    SendBuffer[0] = X11_REQUEST_CREATE_WINDOW;
    SendBuffer[1] = Depth;
    *((int16_t *)&SendBuffer[2]) = RequestLength;
    *((int32_t *)&SendBuffer[4]) = WindowId;
    *((int32_t *)&SendBuffer[8]) = GlobalRootWindow;
    *((int16_t *)&SendBuffer[12]) = X;
    *((int16_t *)&SendBuffer[14]) = Y;
    *((int16_t *)&SendBuffer[16]) = Width;
    *((int16_t *)&SendBuffer[18]) = Height;
    *((int16_t *)&SendBuffer[20]) = BorderWidth;
    *((int16_t *)&SendBuffer[22]) = WINDOWCLASS_INPUTOUTPUT;
    *((int32_t *)&SendBuffer[24]) = GlobalRootVisualId;
    *((int32_t *)&SendBuffer[28]) = X11_FLAG_WIN_EVENT | X11_FLAG_BACKGROUND_PIXEL;
    *((int32_t *)&SendBuffer[32]) = 0xff000000;
    *((int32_t *)&SendBuffer[36]) = X11_EVENT_FLAG_EXPOSURE | X11_EVENT_FLAG_KEY_PRESS;

    int BytesWritten = write(Socket, (char *)&SendBuffer, RequestLength*4);

    return WindowId;
}

int X_MapWindow(int Socket, int WindowId) {
    // TODO: Put this into 'connection' struct
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    SendBuffer[0] = X11_REQUEST_MAP_WINDOW;
    SendBuffer[1] = 0;
    *((int16_t *)&SendBuffer[2]) = 2;
    *((int32_t *)&SendBuffer[4]) = WindowId;

    int BytesWritten = write(Socket, (char *)&SendBuffer, 2*4);
    return 0;
}

void X_OpenFont(int32_t Socket, char *FontName, int32_t FontId) {
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};
    int BytesWritten = 0;
    int BytesRead = 0;

    int32_t FontNameLength = strlen((char *)FontName);
    int32_t Pad = PAD(FontNameLength);
    int RequestLength = (3 + (FontNameLength + Pad)/4);

    SendBuffer[0] = X11_REQUEST_OPEN_FONT;
    SendBuffer[1] = 0;
    *((uint16_t *)&SendBuffer[2]) = RequestLength;
    *((uint32_t *)&SendBuffer[4]) = FontId;
    *((uint16_t *)&SendBuffer[8]) = FontNameLength;
    strncpy(SendBuffer + 12, (char *)FontName, FontNameLength);

    int32_t WriteSize = 12 + FontNameLength + Pad;
    BytesWritten = write(Socket, (char *)&SendBuffer, WriteSize);
}

void X_CreateGC(int32_t Socket, int32_t GcId, int32_t FontId) {
    char SendBuffer[16*1024] = {};

    int32_t CreateGcFlagCount = 3;
    int RequestLength = 4 + CreateGcFlagCount;

    SendBuffer[0] = X11_REQUEST_CREATE_GC;
    SendBuffer[1] = 0;
    *((int16_t *)&SendBuffer[2]) = RequestLength;
    *((int32_t *)&SendBuffer[4]) = GcId;
    *((int32_t *)&SendBuffer[8]) = GlobalRootWindow;
    *((int32_t *)&SendBuffer[12]) = X11_FLAG_FG | X11_FLAG_BG | X11_FLAG_FONT;
    *((int32_t *)&SendBuffer[16]) = 0xFF00FF00; // Foreground
    *((int32_t *)&SendBuffer[20]) = 0xFF000000; // Background
    *((int32_t *)&SendBuffer[24]) = FontId; // Font

    write(Socket, (char *)&SendBuffer, RequestLength*4);
}

void WriteText(int Socket, int WindowId, int GCid, int16_t X, int16_t Y, const char *Text, int32_t TextLength) {
    char Buffer[16*1024] = {};

    uint32_t ContentLength = 4 + (TextLength + PAD(TextLength))/4;

    Buffer[0] = (uint8_t)X11_REQUEST_IMAGE_TEXT_8;
    Buffer[1] = TextLength;
    *((int16_t *)&Buffer[2]) = ContentLength; 
    *((int32_t *)&Buffer[4]) = WindowId;
    *((int32_t *)&Buffer[8]) = GCid;
    *((int16_t *)&Buffer[12]) = X; 
    *((int16_t *)&Buffer[14]) = Y; 

    strncpy(&Buffer[16], (char *)Text, TextLength);
    int BytesWritten = write(Socket, (char *)&Buffer, ContentLength*4);
}

int main(){
    int Socket = socket(AF_UNIX, SOCK_STREAM, 0);
    VerifyOrDie(Socket > 0, "Couldn't open a socket(...)");

    struct sockaddr_un Address;
    memset(&Address, 0, sizeof(struct sockaddr_un));
    Address.sun_family = AF_UNIX;
    strncpy(Address.sun_path, "/tmp/.X11-unix/X0", sizeof(Address.sun_path)-1);

    int Status = connect(Socket, (struct sockaddr *)&Address, sizeof(Address));
    VerifyOrDieWidthErrno(Status == 0, "Couldn't connect to a unix socket with connect(...)");

    int SetupStatus = X_InitiateConnection(Socket);

    if(SetupStatus == 0) {
        int32_t X = 100;
        int32_t Y = 100;
        uint32_t Width = 600;
        uint32_t Height = 300;
        int WindowId = X_CreatWindow(Socket, X, Y, Width, Height);

        X_MapWindow(Socket, WindowId);

        int32_t FontId = GetNextId();
        X_OpenFont(Socket, (int8_t *)"fixed", FontId);

        int32_t GcId = GetNextId();
        X_CreateGC(Socket, GcId, FontId);

        struct pollfd PollDescriptors[1] = {};
        PollDescriptors[0].fd = Socket;
        PollDescriptors[0].events = POLLIN;
        int32_t DescriptorCount = 1;
        int32_t IsProgramRunning = 1;
        while(IsProgramRunning){
            int32_t EventCount = poll(PollDescriptors, DescriptorCount, -1);

            if(PollDescriptors[0].revents & POLLERR) {
                printf("------- Error\n");
            }

            if(PollDescriptors[0].revents & POLLHUP) {
                printf("---- Connection close\n");
                IsProgramRunning = 0;
            }

            char* t1 = "Hello, World!";
            char* t2 = "This is a test text directly written to X";
            char* t3 = "Whooha. Is this even legal? Let's keep a secret!";
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY, t1, strlen(t1));
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY + 15, t2, strlen(t2));
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY + 30, t3, strlen(t3));

            GetAndProcessReply(PollDescriptors[0].fd);
        }
    }

}

Fordítás:

gcc main.c -o main

Ennyi! A linkelt blogposztban részletesen el van magyarázva minden, érdemes elolvasni hozzá.