Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: mhi 09. 04. 2023, 01:31:27
-
Pisu nejaky disassembler pro ne uplne bezny RISCovy procesor. Potrebuju nejak rozumne v cecku udelat to, abych mohl porovnavat masky instrukci (vyznamove bity jsou ruzne rozesety) a soucasne jsem mohl extrahovat jednotlive bity do promennych. Tohle se pise hrozne blbe v cistem C, tak jsem zacal psat program, ktery rozepise kazde volani fiktivni fce bitmatch(var,bitmask[,assignments]) tak, aby vratila nonzero kdyz dojde k matchi instrukce a vyextrahovala mi bity do promennych. Takhle zustava kod celkem slusne citelny a je to syntakticky podobne cecku, takze jdou pouzit indentory, syntax highlighting, apod. Zde je priklad:
if (bitmatch
(opcode, '01010a100aabab.' /*an opcode */ , rd /*target reg */ =
b, rs = a))
{
printf ("ok");
}
vystup:
if (/*.................................................01010a100aabab.*/ ((opcode & 0x7DC0) == 0x2900) && (rd = ((opcode>>1)&0x1)<<0|((opcode>>3)&0x1)<<1,rs = ((opcode>>2)&0x1)<<0|((opcode>>4)&0x3)<<1|((opcode>>9)&0x1)<<3,1))
{
printf ("ok");
}
(neni to jeste dodelane, tohle je fiktivni instrukce, a je pozde a nejsem si jist jestli tam nemam nejakou botu)
Z jineho projektu mam napsany C lexikalni analyzator (tokenizer), ktery jsem na to pouzil, jeho funkce je primitivni, najde dalsi token a naplni strukturu s informacemi o tokenu. Udelal jsem tedy primitivni preprocesor, ktery z meho pseudo-c generuje cecko (lokalizuje identifikator bitmatch a slepe prepise do C kodu):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ctok.h"
void
synterr (struct ctok *tok, const char *whatiswrong)
{
printf ("Syntax error: %s\n", whatiswrong);
exit (1);
}
int
main (int argc, char **argv)
{
struct ctok tok;
int pos, lastwpos;
int i, j, k;
int bs, be, bp;
char varname[0x100], valname[0x100], bitmask[64];
unsigned long long bmmask, bmval;
if (argc != 3)
{
printf ("Usage: %s [infile.d.c] [outfile.c]\n", argv[0]); // xxx outfile se ted nepouziva
exit (1);
}
if (ctok_readfile (argv[1]) <= 0)
{
perror (argv[1]);
exit (1);
}
fprintf (stdout, "/*Automatically generated by %s, DO NOT EDIT! */\n\n",
argv[0]);
lastwpos = pos = 0;
while (ctok_gettoken (pos, &tok, CTOK_FLAG_IGNORECOMMENTS) > CTOK_EOF)
{
// Search for identifier "bitmatch"
if (tok.toktype == CTOK_IDENT && tok.len == 8
&& !memcmp (tok.data, "bitmatch", tok.len))
{
if (lastwpos < pos)
{
fwrite (ctok_getdata (lastwpos), pos - lastwpos, 1, stdout);
}
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_SYMBOL && tok.info != '(')
synterr (&tok, "Expected (");
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_IDENT)
synterr (&tok, "Expected variable identifier");
memcpy (varname, tok.data, tok.len);
varname[tok.len] = 0;
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_SYMBOL || tok.info != ',')
synterr (&tok, "Expected \',\' after variable identifier");
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_CHARLIT)
synterr (&tok, "Expected bitmask");
memcpy (bitmask + 64 - tok.len, tok.data, tok.len);
// ignore spaces in bits, these are just for better readibility
for (i = 0, j = 0; i < tok.len; i++)
if (tok.data[i] != ' ')
bitmask[j++] = tok.data[i];
memmove (bitmask + 64 - j, bitmask, j);
memset (bitmask, '.', 64 - j);
bmval = bmmask = 0;
for (i = 0; i < 64; i++)
if (bitmask[63 - i] == '0' || bitmask[63 - i] == '1')
{
bmmask |= 1ULL << i;
if (bitmask[63 - i] == '1')
bmval |= 1ULL << i;
}
printf ("/*%64.64s*/ ((%s & 0x%llX%s) == 0x%llX%s) && (", bitmask,
varname, bmmask, (bmmask >> 32) != 0 ? "ULL" : "", bmval,
(bmval >> 32) != 0 ? "ULL" : "");
while (1)
{
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_SYMBOL
|| (tok.info != ')' && tok.info != ','))
synterr (&tok, "Expected , or )");
if (tok.info == ')')
break;
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_IDENT)
synterr (&tok, "Expected variable identifier");
memcpy (valname, tok.data, tok.len);
valname[tok.len] = 0;
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_SYMBOL
|| tok.info != '=')
synterr (&tok, "Expected = for bits assignment");
if (ctok_gettoken
(ctok_nexttokpos (&tok), &tok,
CTOK_FLAG_IGNORECOMMENTS) != CTOK_IDENT || tok.len != 1)
synterr (&tok, "Expected bit letter/identifier");
printf ("%s = ", valname);
// calculate bit extraction equations
for (bp = 0, i = 0; i < 64; i++)
if (bitmask[64 - 1 - i] == tok.data[0])
{
bs = i;
while (i < 64 && bitmask[64 - 1 - i] == tok.data[0])
i++;
be = i;
if (bp != 0)
printf ("|");
printf ("((%s>>%d)&0x%X)<<%d", varname, bs,
(1 << (be - bs)) - 1, bp);
bp += be - bs;
}
if (bp == 0)
{
printf ("No matching bits for variable %s\n", valname);
exit (1);
}
printf (",");
} // while new stuff for bit assignment
printf ("1)");
lastwpos = ctok_nexttokpos (&tok);
} // if "bitmatch" identifier token
pos = ctok_nexttokpos (&tok);
}
if (tok.toktype != CTOK_EOF)
synterr (&tok, "C parser error");
if (lastwpos < pos)
{
fwrite (ctok_getdata (lastwpos), pos - lastwpos, 1, stdout);
}
return 0;
}
Cilove prekladace jsou gcc, llvm, msvc.
Ma to 2 problemy:
- idealne bych potreboval nejak vystup poslat do prekladace bez generovani mezisouboru. Videl jsem nejake snahy o vlastni preprocesor ( https://stackoverflow.com/questions/3545875/custom-gcc-preprocessor ), ale takhle to nechci, navic msvc (Windows) bude problem. Idealni by byl nejaky parametr typu '-custom-preprocessor-before-cpp1=...'. Nenasel jsem. Nebo to tam dostat pres pipe. Nenasel jsem jak.
- potrebuju zachovat cisla radek (pocitejme s tim, ze nahrazovane "volani" bitmatch budou i viceradkova, s komentari,atd.) , aby kdyz se seknu nekde v kodu slo hledat chyby (nutne aby to chodilo alespon v gcc/llvm, msvc by bylo fajn, predpokladam ze #line by to nejak mel resit, ale nejak se mi zatim nedari. Jak na to?
Mimo tyto dotazy by mne jeste zajimalo, jestli nejake moderni jazyky poskytuji knihovny, ktere tohle umoznuji napsat s kratsim kodem. C tokenizer vyuziva jen libc a ma pocty radek: 740 ctok.c / 177 ctok.h; jsem takova kozerva, treba mi nekdo ukazete ze s "modernim programovanim" to jde udelat jednoduseji.
-
proc se to blbe pise v cistem c, kdyz mas v c i tokenizer?
udelej bitmatch jako normalni funkci, tu pak muzes oddebugovat pri praci.
a vkladane assignments rd=b, rs=a atd. muzes vyresit pomoci callbackove funkce, kterou do bitmatch posles jako dalsi parametr.
-
Ja bych se na tohle vykaslal a udelat to skrze existujici C preprocesor, pokud to ma byt human-editable:
// opcode table
#define OT_44HL 0xF00F // template
#define OM_nop OT_44HL // mask
#define OV_nop 0xA00B // value
#define OP_IS(x,mask,value) ( ((x)&mask)==value )
Pripadne nadefinoval jeste dalsi makro s pouzitim ## token merge operatoru at nemusis vypisovat mask/value, ale jen suffix k OM_ a OV_
Ale spis bych to osobne videl na machine-generated, tj. nejakym skriptem udelat bootstrap tech defines. Protoze ja jsem moc linej a nerad pisu rucne opakujici se kod, plus to umoznuje rychleji prejit na jinou stavbu kodu, ci doplnit dalsi atributy hromadne.
-
Jsem jenom naivní x86 hobbík, a tak mi určitě uniká jakýsi širší kontext... nicméně téma mě zaujalo a proto mi odpusťte mimózní reakci :-)
Z nápadů typu "generovat zdrojáky v konkrétním jazyce" mívám obecně pocit, že to autor bere za špatný konec. Že by měl raději změnit návrh svého programu směrem k vyšší úrovni abstrakce - že si ve finále usnadní život. Jako chápal bych to, pokud je z nějakého důvodu striktním cílem maximálně kompaktní kód toho "preprocesovaného C" - ale třeba chápat potom chybové hlášky při kompilaci... "more rope to hang yourself with".
Už tady padl dotaz, proč čisté C. Disassembler přece nepotřebuje běžet v omezeném prostředí na nějaké pitoreskní cílové platformě. Sám jste zmínil gcc/clang/MSVC. To jsou přece implementace moderního C++. S mým omezeným rozhledem mi Vaše zadání zatím připadá jako učebnicový příklad na polymorfizmus, OOP, základní C++. Na první pohled nevím, zda by se Vám hodily šablony - spíš bych to viděl na abstraktní třídu "instrukce", z ní dědit jednotlivé typy instrukcí, těm jednotlivě vdechnout nějakého ducha atd. Jasně, objektově se dá psát nakonec i v holém C, obvykle pokud je součástí zadání "žádné C++".
Společná abstraktní třída "instrukce" by mohla mít statickou metodu "hrubý rozskok, co je zač následující instrukce". Každá "podtřída instrukce" by měla implementovánu metodu "disasm", která by dostala pointer na první bajt ve vstupním bináru a vrátila by např. pointer na první bajt následující instrukce (nebo chybu). Disassembler by vyráběl obdobu "lexikálního stromu" - akorát by to byla spíš sekvence. Případně by se v tom pak daly identifikovat "emergentní sémantické struktury" = skoky, smyčky, začátky+konce funkcí, práci s pojmenovanými proměnnými apod. Pokud má být výstupem sekvence ASM kódu (text), může mít každá "podtřída instrukce" svou vlastní metodu "print" nebo tak něco...
Chápu, že ortodoční ASM/céčkař může namítat, že všechno to C++ lešení je strašně moc datlování navíc. A ozvou se tu možná lidi, že to je úloha na *funkcionální* programování. Tak jako tak: Inu proti gustu... A ano, málo jsem toho zažil.
Už jsem nějaký disassembler držel v ruce (jako uživatel) a rozumím tomu, že třeba není vůbec sranda, najít začátky sekvencí executable instrukcí v bináru, kde je kód promíchaný s daty. Základní vodítka (třeba nějaký ten entry point, rozdělení na "segmenty"?, nedejbože seznam symbolů) může poskytnout formát binárního image (souvisí s linkerem), což asi platí spíš pro ustálené formáty vyšších OS... Vyšší jazyky mají ustálené "function call conventions", což ale neplatí pro volnou tvorbu v assembleru... Máte s tím tématem moje sympatie. Když vidím, s čím si (ne)poradí třeba IDA free... = I s ohledem na případné hledání nějakých vyšších struktur mám pocit, že "zaobjektění" by mohlo být ku prospěchu této následné další práce.
-
Rada: napiš si databázi instrukcí a podle toho vygeneruj celý disassembler.
-
Rada: napiš si databázi instrukcí a podle toho vygeneruj celý disassembler.
Ano! :-) A začít je třeba symbolickým jazykem pro metapopis opcodů, operandů, multiplexovaných bitů, a jak z toho poskládat oficiální ASM zápis.
-
Dekuji za odpovedi, prestoze tedy neodpovidaji na me otazky, ale budiz. Otazky jsou stale aktualni :).
Duvody proc to chci takhle jsou asi 2, prvni je aby to bylo rychle; ten disassembler je i "analyzujici", a ja jsem liny clovek, takze celkem hrubou silou budu hledat zpetne treba kde se pushuji argumenty na stack, bez toho abych tam delal nejake slozitejsi veci. Takze nejake neustale prochazeni toho bitstringu v nejake fci je neunosne, i moje cesta je celkem pomala, mozna bude nutne "vetsi kalibr" ve forme nejakeho rozeskakavani (prirazeni od promennych jsem v minulosti resil pres varargs, argumenty vzdy v paru int* kamToUlozit a char oznacenibitu, callback neni nutny). Druha vec je prehlednost, kod ktery treba navrhuje RDa pro mne neni jednoduse naprogramovatelny bez chyb, a neni prilis snadno udrzovatelny. Muzu sem dat priklad jak vypada treba risc-v, ktery je celkem slusne navrzeny a neni to obvykle cunecina typu "kdyz jsou v instrukci XYZ nastaveny bity 567 do 101 tak je to vlastne uplne jina instrukce ABC :)". I tak je to uz na hrane, kdyz tam clovek pak chce neco pridat, musi premyslet kam presne to dat aby neco nerozbil.
//0010011 ADDI, SLTI, SLTIU, XORI, ORI, ANDI
//0010011 SLLI, SRLI, SRAI
if ( (opcode & 0x7F) == 0x13) {
switch ((opcode>>12) &7) {
case 0b000: opname = "addi"; break;
case 0b010: opname = "slti"; break;
case 0b011: opname = "sltiu"; break;
case 0b100: opname = "xori"; break;
case 0b110: opname = "ori"; break;
case 0b111: opname = "andi"; break;
default: opname = NULL; break;
}
simmval = SIGNEXT( ((opcode>>20)&0xFFF),12);
if (opname) {
if (rel != NULL && ELF32_R_TYPE (rel->r_info) == R_RISCV_LO12_I && (rel->r_addend&0xFFF) == (simmval&0xFFF)) {
sprintf (line, "%s %s, %s, %%lo(%s)", opname, riscv_regnames[bits7_11], riscv_regnames[bits15_19], fmt_opt_addend(get_symbol_name (ELF32_R_SYM (rel->r_info)), rel->r_addend));
*relparsed = 1;
} else {
sprintf (line, "%s %s, %s, %s", opname, riscv_regnames[bits7_11], riscv_regnames[bits15_19], fmt_hexneg(simmval) );
}
return 4;
}
if ( ((opcode>>12) &7) == 0b001 && ((opcode>>25) &0x7F) == 0 )
opname = "slli";
if ( ((opcode>>12) &7) == 0b101 && ((opcode>>25) &0x7F) == 0 )
opname = "srli";
if ( ((opcode>>12) &7) == 0b101 && ((opcode>>25) &0x7F) == 0b0100000 )
opname = "srai";
if (opname) {
sprintf (line, "%s %s, %s, 0x%03X", opname, riscv_regnames[bits7_11], riscv_regnames[bits15_19], ((opcode>>20)&0xFF) );
return 4;
}
}
Prumerne bych odhadnul ze udelam kazdou ctvrtou-patou instrukci nejakou chybu behem pocitani tech bitshiftu/andu/permutaci a uz mne to moc nebavi, ten tool je cesta k vysledku, ne smysl meho zivota.
Nechapu taky co bych ziskal implementaci v C++, krome toho ze bych to nemel na tisicovce (dvou) radek, ale treba v trech stech tridach ? Z toho mi uplne beha mraz po zadech. Ale je mozne, ze jsem celou teorii okolo OOP vlastne prilis nepochopil. Kdyz jsem psal ten C lexer, pri cteni vlastnosti modernich C++ jsem jenom ziral a ujistoval se, ze to neni nejaky omyl. Zajimalo by mne, jestli existuje na tehle planete nejaky clovek, ktery skutecne vsemu v C++ rozumi a je schopen rict "co se stane kdyz ..."
-
a co treba nemenit puvodni kod, ale udelat si samostatny nastroj analyzator/syntezator, ktery to zanalyzuje do jineho souboru. kdyz pak budes potrebovat zmenit opkod apod. tak druhy nastroj ti z detailu slozi upraveny zhusteny kod, ten jen nakopcis do puvodniho mista a prebuildis.
-
Ja by som to kvôli prehľadnosti riešil tak ako to napísal František Ryšánek, teda cez OOP a polymorfizmus. Radšej budem mať 300 tried a viem, čo každá má robiť a ako to má robiť a pre aké dáta, ako nejaký x tisíc riadkový program, v ktorom sa stratím do 30 sekúnd.
-
já už jsem dělal něco podobného a už nikdy bych to nedělal takto, sorry.
Potřebuješ DB těch instrukcí a potřebuješ vygenerovat ten disasm kód - jakákoliv jiná cesta znamená:
- ztráta času, budeš udržovat v podstatě neudržitelný kód a pořád přemýšlet nad tím jak udělat líp něco, co nejde... znásilňovat C preprocessor až do absurdit a ptát se na rootu jak to udělat líp - ale nikdo ti stejně nepomůže...
- nové instrukce tě budou stát hodně času, a možná budeš díky podpoře nových instrukcí rozbíjet ty staré
- testování - pokud vygeneruješ disassembler na základě nějakých dat, tak si můžeš vygenerovat i testovací sadu (neříkám, že to je nelpepší, já bych sáhnul ještě po LLVM testovací sadě v tomto případě)
Podívej se na nějaké projekty, které mají assembler/disassembler - LLVM/capstone/keystone, zydis...
Výstup toho disassembleru by měla být nějaká reprezentace a ne text - instrukce (nějaké ID), metadata, a operandy. s tím se pak dá i nějak pracovat, analyzovat, změnit, a popř. prohnat přes encoder a mít upravenou instrukci, atd...
-
Již zmíněný capstone podporuje i RISCV, nebylo by řešením použít tohle? https://www.capstone-engine.org/
-
Dá se na tom slušně vydělat nebo to je "zadmózní" projekt?
-
Takze vse vyresim humpolackou metodou, radky tak, ze udelam spravny pocet newline za svym prepsanym kodem, aby to odpovidalo zdrojaku. A necham to at si vsechno resi pravidla Makefile, pro MSVC asi pres pre-compile command.
Prijde mi zajimave jak jsem dostal hromadu odpovedi ... ale ne na moje otazky :).
CPU: Na tvorbe C pre-precompileru bych svoji karieru asi nestavel.
-
Takze vse vyresim humpolackou metodou, radky tak, ze udelam spravny pocet newline za svym prepsanym kodem, aby to odpovidalo zdrojaku. A necham to at si vsechno resi pravidla Makefile, pro MSVC asi pres pre-compile command.
Upřímně, tohle mi zní jako ohýbák bez narovnáváku.
Prijde mi zajimave jak jsem dostal hromadu odpovedi ... ale ne na moje otazky :).
Dostal jsi spoustu relevantních odpovědí, které ti říkají, proč nemáš dělat to, co se snažíš dělat. Pokud v tom chceš pokračovat, tak to je samozřejmě tvoje volba, jen potom nemůžeš očekávat, že dostaneš odpovědi na tvoje otázky.
-
Najlepsie je pouzit nieco hotove, napr Capstone (https://www.capstone-engine.org/) a C (https://www.capstone-engine.org/lang_c.html) alebo Python (https://www.capstone-engine.org/lang_python.html)
-
Hoši děkuju za tohle vlákno :-) Konkrétně Capstone je veliký rozcestník na další čtení. Obor nějak soustavně nesleduju, takže mě překvapilo, kolik volně dostupného softu kolem toho je. A přestože jsem už notně off topic, zmínil bych třeba tuhle paní - ta zjevně umí:
https://hasherezade.github.io/
https://www.youtube.com/@hasherezade/videos
https://hshrzd.wordpress.com/how-to-start/
-
Problem techto nastroju je, ze kdyz chce clovek neco specifickeho, stoji casto vic casu jejich prepracovani, nez udelat vyvoj "skoro od nuly". Myslim tim to, ze nekde najdu vhodny zdroj opkodu a ten pouziju-napsat jednoduchy disasm pro novou architekturu je pak otazka odpoledne. Diky nejakemu odkazu tady jsem objevil, ze Ghidra ma slusnou definici opkodu pro TriCore, ktery ted budu potrebovat udelat.
Mam tu ale dalsi tema a to je indentace assembler kodu (podobne jako IDA umi udelat strom). Hezky to obcas dela ChatGPT, zde je priklad:
; Load array address into r0
LDR r0, =ARRAY
; Load array size into r1
LDR r1, =SIZE
; Subtract 1 from array size
SUBS r1, #1
; Loop outer
outer_loop:
; Move outer loop index to r2
MOV r2, r1
; Move outer loop index to r3
MOV r3, r1
; Loop inner
inner_loop:
; Load ARRAY[r2].x into r4
LDRB r4, [r0, r2]
; Load ARRAY[r2].y into r5
LDRB r5, [r0, r2, LSL #1]
; Load ARRAY[r3].x into r6
LDRB r6, [r0, r3]
; Load ARRAY[r3].y into r7
LDRB r7, [r0, r3, LSL #1]
; Compare x values
CMP r4, r6
; If ARRAY[r2].x >= ARRAY[r3].x, skip swap
IT GE
MOVGE r9, r2
MOVGE r2, r3
MOVGE r3, r9
; Compare y values
CMP r5, r7
; If ARRAY[r2].y >= ARRAY[r3].y, skip swap
IT GE
MOVGE r9, r2
MOVGE r2, r3
MOVGE r3, r9
; Decrement inner loop index
SUBS r2, r2, #1
; Loop inner while inner loop index >= 0
BGE inner_loop
; Decrement outer loop index
SUBS r1, r1, #1
; Loop outer while outer loop index >= 0
BGE outer_loop
; Return
BX lr
Mate nejaky napad na pravidla jak tohle delat? Jsou jasne ruzne if-else-endif, pokud jdou po sobe, ale ne vzdy to tak je. Uvital bych odkazy na pripadne dalsi nastroje, ktere tohle nejak umi. ChatGPT jsem se ptal jestli mi napise pravidla ktera pouziva a kdybych veril ze tam je "rozum", tak bych rekl, ze "jsme si vubec neporozumeli" :-). Ani na nekolikaty pokus ji vysvetlit co mne zajima.
-
ChatGPT jsem se ptal jestli mi napise pravidla ktera pouziva a kdybych veril ze tam je "rozum", tak bych rekl, ze "jsme si vubec neporozumeli" :-). Ani na nekolikaty pokus ji vysvetlit co mne zajima.
Což je mimochodem *velice* zajímavý poznatek. Člověk by řekl, že tu věc má "naučenou hrubou silou v motorické paměti", asi jako základní úkony řízení auta nebo jízdu na lyžích. "Držení rovnováhy" taky těžko vysvětlíte.
Přitom ale v jiných oblastech dokáže zdůvodňovat docela hezky...
Toto si dokážu vysvětlit dvěma různými způsoby:
A) GPT4 řeší různé činnosti/oblasti různými způsoby. Proto má některé "v motorické paměti" a jiné umí přesně spočítat a zhruba zdůvodnit
B) narazil jste na oblast, kterou vysvětlovat nechce/nesmí = cenzura, obchodní tajemství. Když umí zdvořile odmítnout odpověď na "sprostá témata", proč by nemohl odmítnout odpověď na témata obchodně hodnotná...
-
Problem techto nastroju je, ze kdyz chce clovek neco specifickeho, stoji casto vic casu jejich prepracovani, nez udelat vyvoj "skoro od nuly". Myslim tim to, ze nekde najdu vhodny zdroj opkodu a ten pouziju-napsat jednoduchy disasm pro novou architekturu je pak otazka odpoledne. Diky nejakemu odkazu tady jsem objevil, ze Ghidra ma slusnou definici opkodu pro TriCore, ktery ted budu potrebovat udelat.
Mam tu ale dalsi tema a to je indentace assembler kodu (podobne jako IDA umi udelat strom). Hezky to obcas dela ChatGPT, zde je priklad:
; Load array address into r0
LDR r0, =ARRAY
; Load array size into r1
LDR r1, =SIZE
; Subtract 1 from array size
SUBS r1, #1
; Loop outer
outer_loop:
; Move outer loop index to r2
MOV r2, r1
; Move outer loop index to r3
MOV r3, r1
; Loop inner
inner_loop:
; Load ARRAY[r2].x into r4
LDRB r4, [r0, r2]
; Load ARRAY[r2].y into r5
LDRB r5, [r0, r2, LSL #1]
; Load ARRAY[r3].x into r6
LDRB r6, [r0, r3]
; Load ARRAY[r3].y into r7
LDRB r7, [r0, r3, LSL #1]
; Compare x values
CMP r4, r6
; If ARRAY[r2].x >= ARRAY[r3].x, skip swap
IT GE
MOVGE r9, r2
MOVGE r2, r3
MOVGE r3, r9
; Compare y values
CMP r5, r7
; If ARRAY[r2].y >= ARRAY[r3].y, skip swap
IT GE
MOVGE r9, r2
MOVGE r2, r3
MOVGE r3, r9
; Decrement inner loop index
SUBS r2, r2, #1
; Loop inner while inner loop index >= 0
BGE inner_loop
; Decrement outer loop index
SUBS r1, r1, #1
; Loop outer while outer loop index >= 0
BGE outer_loop
; Return
BX lr
Mate nejaky napad na pravidla jak tohle delat? Jsou jasne ruzne if-else-endif, pokud jdou po sobe, ale ne vzdy to tak je. Uvital bych odkazy na pripadne dalsi nastroje, ktere tohle nejak umi. ChatGPT jsem se ptal jestli mi napise pravidla ktera pouziva a kdybych veril ze tam je "rozum", tak bych rekl, ze "jsme si vubec neporozumeli" :-). Ani na nekolikaty pokus ji vysvetlit co mne zajima.
v principu to je jen problem "balanced bracket", takze bych googlil algoritmy na tohle.
-
Mate nejaky napad na pravidla jak tohle delat?
Heuristikou detekovat co je loop (tj. label - codeblock - jumpback) a treba if/then/else, a to indentovat. jakekoliv labely a jumpy navic jsou netypicke a nechaj je jak jsou.
Takze rozparsovat, analyzovat, pattern matching, rule priorities - pridani atributu a pak uz jen vystupni re-formatovani.
Tohle je celkem straight-forward, bez nutnosti nasazovani "AI u ktere netusime co provede".
Ale prvne bych se podival, zda nejaky "asm prettyprint" neexistuje, cim dal tim vice jsou vsechny bezne problemy pokryte.
-
Ja to nemyslel ze bych to resil pres AI, ale ze konkretne ChatGPT to obcas tiskne moc hezky a vidim v tom nejaky smysl.
To co pises je celkem jasne, ale prave ta pravidla jak to delat uz tak jasna nejsou. Kdyz si vezmu ruzne ukazky praktickych kodu, tak obcas sam nevim jak to udelat "hezky". Treba kde se v loopu skace tam-zpet a je tam treba nejaka dvojita podminka ... tohle nevim jak udelat aby to bylo dostatecne čitelné (ani rucne v editoru nemam jasno, dabel je v tech detailech).
Ona ta indentace asm je takova nouzovka pro zprehledneni, ale je to "zadarmo" - zrovna to bych mohl trivialne implementovat, moje disassemblery oznacuji skoky i terminacni instrukce (jump && !terminace ==> podmineny skok) pro analyzu flow.
-
To co pises je celkem jasne, ale prave ta pravidla jak to delat uz tak jasna nejsou. Kdyz si vezmu ruzne ukazky praktickych kodu, tak obcas sam nevim jak to udelat "hezky". Treba kde se v loopu skace tam-zpet a je tam treba nejaka dvojita podminka ... tohle nevim jak udelat aby to bylo dostatecne čitelné (ani rucne v editoru nemam jasno, dabel je v tech detailech).
Tak porad mas prostor to implementovat i vertikalnim odsazenim - to co je zrejma soucastast condition v if/then/else, tak muzes slepit dokupy, a bloky kodu odsazovat.
To vertikalni odsazovani jsem pouival ja - call target = 2-3 radky, jump target = 1 radek (pred navestim). Horizontalne me to nenapadlo odsazovat.. ale ja tam mel rovnou i dump adres a hexa kodu, podobne jak to dela treba IDA. A pak jsem tam spis pridaval komentare - zpetne reference, odkud se sem skace. Takze bylo poznat ze neco s 1 referenci je neco jineho.. nez neco s hromadou referenci.
-
Potrebujes ten asm kod rozsekat na "basic blocks" (BB), z toho si udelas control flow graph (CFG) a pak uz jen pouzivas algoritmy na to urcene (klicove slova treba "loop detection in CFG"):
prvni odkaz na google:
https://pages.cs.wisc.edu/~fischer/cs701.f14/finding.loops.html
Doporucil bych nastudovat graph theory, na tyto veci se hodi.
-
Tohle forum je opravdu zajimave. Clovek si tak rika, jestli autor prispevku vubec cetl na co reaguje.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
RDa: oddeleni pomoci newline mam, sice trosku jinak, ale ano, takhle to delam. Mne jde skutecne jen o tu indentaci. Staci si (napr.) v IDA otevrit defacto jakoukoliv binarku se slozitejsim optimalizovanym algoritmem (nejake zanorene cykly a if-y v sobe) a k tomu se zamyslet nad indentacnimi pravidly. Primitivni pravidlo ze telo kazdeho if-else-endif a while-endwhile indentuju o jednu uroven neni uplne nejprehlednejsi. ChatGPT tohle umi mnohem lip, akorat tam nic nedisassembluje, ale generuje novy kod, kde ten vyznam zna, a indentaci to AI nekdo zrejme naucil (otazka je zda sikovnymi pravidly, nebo opet pres uceni).
-
Tak realny kod ale muze byt v binarce taky out of order - takze to bude skakat sem tam a tohle nezaindentuje ani clovek, natoz stroj.
Takze si vem par jednodussich pravidel a zkus je aplikovat na svuj code flow na vystupu disasm