[EA][ASM][Tool] lyn (elf2ea if you will)

lyn: “convert” .elf object files to EA events

(If you don’t get it yet keep reading it gets interesting)
(Actually if you never got into asm you can stop now but otherwise bear with me)

Usage:
lyn [-nolink/-linkabs/-linkall] [-longcalls] [-raw] [-printtemp] [-autohook] <elf...>

Note: this is still fairly WIP: not a lot of testing have been done, and it will get at least a few extra updates to include extra functionnalities (see end of post for planned features list).

This tool is fairly simple at first glance: it takes an elf file as argument, and outputs EA code to stdout. But there’s a few things it does that will (hopefully) really change the way we’d go about asm hacking with EA.

But first, let’s clarify a few things:

What’s an elf?

ELF (Executable and Linkable Format) files are what your assembler or compiler will spit out, and what linkers will take to form executables. It contains various useful information, such as of course the compiled asm, but also a bunch more such as symbols and relocations that are specifically useful for linking.

The “old” method of inserting asm into the ROM with EA was simply to extract from that elf the assembled binary part and #incbining it. While this worked, it also invoked the need to write a bunch of other instructions around that to properly link the asm to the rest of the hack/game (labels, post-asm literals, etc), and also was somehow limited (only one routine per file, cannot use relative jumps between files (such as bls), etc).

What lyn tries to do is to bring the functionalities of a proper linker to EA. That is (for now): defining elf global symbols as labels, and “converting” relocations (for example, a BL to SomeSymbol) to EA code (same example: BL(SomeSymbol)).

Why “lyn”?

linker>link>lin>lyn? Since Lyn a FE character & EA is a FE hacking tool it felt somewhat fitting. I probably wanted to it to be a special snowflake and not to be named elf2ea or something like every other tool ¯\_(ツ)_/¯

How2use???

On its own

You can in theory just drop your elf file onto the lyn executable, but what that will do is just write the output to a console and immediately close said console. What you can do is write a bat file that will call lyn for the argument file, and output the whole thing to another file (probably using redirections). Here’s an example windows batch file (assumes lyn is in the same folder):

@echo off
"%~dp0lyn" "%~1" > "%~n1.event"
pause

Save that as a .bat file (again, in a folder with lyn in it) and drop an elf onto it to generate a corresponding .event file.

Using EA inctext

Quick reminder: what #inctext does is include the result of the invoked command as EA code (as opposed to #incext that includes it as raw binary data).

First we want to have the lyn executable in the <Event Assembler>/Tools folder, alongside the other tools like Png2Dmp, ParseFile, PFinder, etc.

Then we can simply in any event code use the following code to include the EA-ified elf file:

#inctext lyn "relative/path/to/some/file.elf"

Example of small asm hack that uses lyn

Ok so here I will walk us through remaking my “LolStats” hack by using lyn (“LolStats” was a quick experiment hack that changes the way stat increase/growth work)

First, here’s the asm code I used (it’s a straight replacement for the routine at 0x02B9A0, that I labelled “GetStatIncrease”)

ASM
.thumb

.set NextRN_100, 0x08000C64

@ Arguments: r0 = Growth
@ Returns: r0 = Stat Increase
GetStatIncrease:
	push {r4-r5, lr}
	
	mov r4, r0
	mov r5, #0
	
Continue:
	ldr r3, =NextRN_100
	mov lr, r3
	.short 0xF800
	
	sub r4, r0 @ r4 = (r4 - RN100)
	blt End    @ if (r4 < 0) goto End;
	
	add r5, #1 @ stat++
	
	b Continue
	
End:
	mov r0, r5
	
	pop {r4-r5}
	
	pop {r1}
	bx r1

Contains some labels, standard push/pop, a blh to a vanilla routine, etc… Fairly standard. Next, here’s the event file I used up until now:

Events
#include "Extensions/Hack Installation.txt"

{
PUSH; ORG 0x2B9A0
	replaceWithHack(GetStatIncrease)
POP

ALIGN 4
GetStatIncrease:
	#incbin "asm/GetStatIncrease.bin"
}

The first thing we are going to do is replace the whole Label: #incbin stuff by a #inctext to lyn:

Events but now with lyn
#include "Extensions/Hack Installation.txt"

{
PUSH; ORG 0x2B9A0
	replaceWithHack(GetStatIncrease)
POP

ALIGN 4
#inctext lyn "asm/GetStatIncrease.elf"
}

Note: Hack Installation is required for lyn to work too! This may change in the future tho

BUT if you only do that, you’ll get an error from EA saying that GetStatIncrease isn’t in scope, which is normal, since we need to tell our assembler to “expose” the label. So back to our asm source, and add the following:

.global GetStatIncrease
.type   GetStatIncrease, %function

Whereever you put it, it doesn’t really matter. What it does is that is tells the symbol GetStatIncrease is global and is of type “function”. This is required information for lyn (and any proper linker really) to know which symbol to expose (make as label) and in what way.

If you add that, assemble to elf, and assemble the events, and it should have worked! You could use the GetStatIncrease label defined in the asm source from EA! Of course that’s not all lyn does, since it also applies relocations: that means is that if you used in another asm source included through lyn the operation bl GetStatIncrease, it would have worked!

And… what if I told you that using lyn would also allow use to make use of elf files generated by something that’s not a straight assembler with way more ease… Anyway that’s probably a story for another time.

Here is the final & complete version of my “lyn-ified” LolStats hack.

See next post for follow-up guide stuff:

BONUS: How to get an elf?

Here's your usual Assemble ARM.bat
@echo off

SET startDir=%~p0\devkitARM\bin\

@rem Assemble into an elf
SET as="%startDir%arm-none-eabi-as"
%as% -g -mcpu=arm7tdmi -mthumb-interwork %1 -o "%~n1.elf"

@rem Extract raw assembly binary (text section) from elf
SET objcopy="%startDir%arm-none-eabi-objcopy"
%objcopy% -S "%~n1.elf" -O binary "%~n1.dmp"

echo y | del "%~n1.elf"
pause

You can see there’s a specific part that generates an elf. Isolate that and you’re good! See:

There
@echo off

SET startDir=%~p0\devkitARM\bin\

@rem Assemble into an elf
SET as="%startDir%arm-none-eabi-as"
%as% -g -mcpu=arm7tdmi -mthumb-interwork %1 -o "%~n1.elf"

pause

Known Limitations

Because of how EA handles pointers & offsets, I cannot make lyn relocate to anything that isn’t in the ROM (I’m mainly thinking RAM data), this is a problem that would probably be less of one once I add the ability for “anticipated linking” between elfs (we’d then just need to add an elf that contains absolute symbols pointing to said RAM data).

Planned features

  • Bugfixes
  • Support more relocations (such as ARM ones)
  • More if ideas or suggestions come (feel free to suggest!)

As always, if you have any question, bug report or anything to ask/tell me, feel free to do so in this thread or on the FEU Discord!

Thanks to @MisakaMikoto and his thread of notes on asm calls for helping me understand some things faster and give me an idea of how to implement some extra functionalities later.

Other references used in coding the thing:

Have a great day! - StanH_

11 Likes

Second release of lyn (well third because I fucked up n°2 but shh)

  • Removed dependency on the Hack Installation EA extention (BL -> a bunch of SHORTs)
  • Added program options (see below)
  • Allowed processing multiple elves (lol) together, and anticipate linking when applicable
  • Allowed forcing external BL calls to be converted to absolute calls through “trampolines” (“veneers” as the official ARM ELF spec calls them)
  • Allowed automatic hooking into vanilla routines (if both an absolute & a local symbol of the same name exist, and said absolute symbol points to ROM, then lyn will automatically output a hook the local symbol wherever the original absolute symbol pointed to)

Usage (new):

lyn [-nolink/-linkabs/-linkall] [-longcalls] [-raw] [-printtemp] [-autohook] <elf...>
  • -nolink/-linkabs/-linkall defines linking policy. Behavior may conflict when other options are set (other options usually have priority).
    • -nolink prevents any anticipated linking to occur (all relocations will be exposed to EA)
    • -linkabs will only link absolute symbols (ie symbols that cannot simply be represented as labels using EA)
    • -linkall will link everything it can (absolutes, relatives, locals…) (set by default)
  • -longcalls will transform any direct BL call (and maybe later other calls/jumps) to a call to a “trampoline”, where the target symbol will be represented as absolute.
  • -raw will prevent any anticipated computation to occur, exposing the elves as is.
  • -printtemp will expose temporary symbols (for now this only includes labels associated with trampolines)
  • -autohook will allow automatic hooking into vanilla routines (see above for details). On by default (disable it with -raw)

Also since it has been brought to my attention that my examples actually didn’t show what lyn actually does, here’s a brief extra of the OP walk-through:

EXTRA: What lyn does

Let’s assume the following source:

Same ASM as before more or less
.thumb

.global GetStatIncrease
.type   GetStatIncrease, %function

@ Arguments: r0 = Growth
@ Returns: r0 = Stat Increase
GetStatIncrease:
	push {r4-r5, lr}
	
	mov r4, r0
	mov r5, #0
	
Continue:
	ldr r3, =NextRN_100
	mov lr, r3
	.short 0xF800
	
	sub r4, r0 @ r4 = (r4 - RN100)
	blt End    @ if (r4 < 0) goto End;
	
	add r5, #1 @ stat++
	
	b Continue
	
End:
	mov r0, r5
	
	pop {r4-r5}
	
	pop {r1}
	bx r1

Let it go through lyn and you get this:

lyn output
PUSH
ORG (CURRENTOFFSET+$1); GetStatIncrease:
POP

SHORT $B530 $1C04 $2500 $4B05 $469E
BYTE $00 $F8
SHORT $1A24 $DB01 $3501 $E7F8 $1C28 $BC30 $BC02 $4708
POIN NextRN_100

syntax highlighting colors didn’t explode for once lol

So what happened here is that

  • assembled asm got inserted using SHORTs (thumb opcodes are 2 bytes, so it used SHORTs because why not) (the one BYTE $00 $F8 part is because of the .short 0xF800, which is the second part of the bl opcode)
  • the one global symbol in our asm got exposed (GetStatIncrease) as a label (the +$1 is because of the thumb bit. tl;dr if you expose a symbol that “points” to thumb code, then that symbol will automatically gain the thumb bit)
  • the one reference to an external symbol (aka relocation) got exposed (POIN NextRN_100)

Ok so that’s what lyn does, and it was all it did until today’s update.

Going further with lyn

With today’s shiny new features, we can basically write a simple hack that replaces a vanilla routine using only a lyn call!

Ok so first, here’s another file, that we are going to call the “definitions” file:

Definitions.s

.macro SET_FUNC name, value
	.global \name
	.type   \name, %function
	.set    \name, \value
.endm

SET_FUNC NextRN_100,      (0x08000C64+1)
SET_FUNC GetStatIncrease, (0x0802B9A0+1)

It’s basically defining the offsets of some vanilla routines, with the thumb bit (the thumb bit is kinda important).
What the elf version of this file does is expose those values as absolute symbols, which won’t be exposed to EA, but will be very useful when it comes to anticipated linking.

So how about we call lyn one last time so I can prove my point (make sure to have assembled your source files to elves!)

lyn "GetStatIncrease.elf" "Definitions.elf"
This is what we get
PUSH
ORG $2b9a0
SHORT $4778 $46C0
WORD $E59FC000 $E12FFF1C
POIN GetStatIncrease
POP
PUSH
ORG (CURRENTOFFSET+$1); GetStatIncrease:
POP

SHORT $B530 $1C04 $2500 $4B05 $469E
BYTE $00 $F8
SHORT $1A24 $DB01 $3501 $E7F8 $1C28 $BC30 $BC02 $4708
BYTE $65 $0C $00 $08

Ok so first big difference from what we had before is there’s a bunch of new stuff at the start of the output. A PUSH, an ORG to a fixed location… You guessed it: it’s a hook into a vanilla routine. This is what the new automatic hooking feature allows us to do. Here’s an asm version of whatever that is:

common lyn trampoline
.thumb
bx pc @ switch to ARM
nop @ .align
.arm
ldr r12, =GetStatIncrease @ load target in r12 (ip)
bx r12 @ goto r12 (ip)
.word GetStatIncrease

Note: this piece of ASM is according to the ARM ELF spec a valid “ip-corrupting veneer”, which is the kind of extra piece of code a linker is allowed to insert to allow for long jumps & arm/thumb switches between different object files

I said common lyn trampoline since it’s the same that is used when the -longcalls option is specified.

Anyway what this does is hook into a vanilla call and redirect to our own GetStatIncrease routine. In effect it replaces the routine.

Other than that, not much different from before… except maybe for the fact that the NextRN_100 reference is… gone! Well that would make sense since we specified it’s value in our Definitions elf and when you look closed, you’ll find a BYTE $65 $0C $00 $08 part in the EA output… yes there it is! It may not seem like much but it allows us to bypass having to define the value of it in both EA & our original source… Which is especially useful in case you’re working with a elf-generator that doesn’t allow for defining any value for any symbol.

With all of this, it reduces our event file to do the following:

Final events

{
	ALIGN 4
	#inctext lyn "asm/GetStatIncrease.elf" "asm/Definitions.elf"
}

…Oh. All our hack installer has become is a call to lyn… Well that’s mission accomplished! We made a small hook hack using only a lyn call!

Phew. Now that’s done, let me tell you a thing: I’m probably done with adding major features to lyn at this point. Any further update will probably be small additions & fixes. I do plan on working on one or more tools to go with the lyn-based hacking workflow, and after that maybe coming back to tea, and after that I may do some actual hacking (lol).

Have another great day! - StanH_

5 Likes

Wow this really would’ve been nice to know about at the time.
damn…

1 Like