bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Assembly challange

Thu Nov 29, 2018 7:55 pm

Dear All,

I'd like to propose a very fun challange. We are looking for the Assembly equivalent of

Code: Select all

if (AArch32) {
    - or -
if (AArch64) {
    - or -
if (!AArch32) {
    - or -
if (!AArch64) {
What makes this challange funny is that the resulting binary must work on both A32 and A64 just as-is. It either has to do nothing (or do irrelevant or insignificant operations without throwing an abort) on 32 bit platforms, and branch on A64, or vice-versa. It doesn't matter if the Assembly can be compiled on A32 or on A64, or if it uses only ".long" directives. The point is, only one interpretation (either 32 or 64) must branch, and not the other. If the same binary could be interpreted as a valid Thumb32 code too, that's a bonus, but not required.

You should read the ARM DDI0487 documentation to find the answer, but other sources can be used as well. If there's no solution until new year's eve, I'll present mine. If there are multiple solutions, then the shorter wins, or the one that also works on T32 and does the same as in A32 mode. If all are the same size and all are supported by the same execution modes, then I'll let the community decide which one is more elegant, and in lack of that, the first posted wins.

Good luck and happy opcode hunting! :-)
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Fri Nov 30, 2018 1:21 am

There is an obvious solution which is to pseudocode the opcodes, is that a valid or are we restricted to actual true assembler?
To some degree GCC already has pseudocoding in things like "ldr x1, =somevar"

The obvious most elegant answer to me is a simple include of a file with the assembler in it, there is exactly one if statement required.
I do it all the time with SmartStart32.S and SmartStart64.S being my omipresent library functions which are matched :-)

User avatar
Paeryn
Posts: 2225
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Assembly challange

Fri Nov 30, 2018 6:50 am

LdB wrote:
Fri Nov 30, 2018 1:21 am
There is an obvious solution which is to pseudocode the opcodes, is that a valid or are we restricted to actual true assembler?
To some degree GCC already has pseudocoding in things like "ldr x1, =somevar"

The obvious most elegant answer to me is a simple include of a file with the assembler in it, there is exactly one if statement required.
I do it all the time with SmartStart32.S and SmartStart64.S being my omipresent library functions which are matched :-)
I don't think bzt means one source file that will compile for either instruction set, rather they want one single binary file which can be executed regardless of whether the processor is in A32 or A64 (and possibly even T32) mode whilst using the same entry point. So you would need to choose instructions carefully so that when it is read in say A64 it jumps over the A32 code and when in A32 it would run the A32 code and then jump over the A64 code.

An interesting experiment.
She who travels light — forgot something.

User avatar
DavidS
Posts: 3698
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Assembly challange

Fri Nov 30, 2018 6:58 pm

If only I had an assembler that supported AARCH64, sounds like a fun challenge, create a FAT binary for the ARM AARCH32 and AARCH64, should be interesting.
The Raspberry Pi is an ARM computer, that runs many Operating Systems, including Linux, RISC OS, BSD, Pi64 as well as many more.
Soon to add AROS, and TOSARM to the list of operating systems.

jahboater
Posts: 3293
Joined: Wed Feb 04, 2015 6:38 pm

Re: Assembly challange

Sat Dec 01, 2018 12:03 pm

DavidS wrote:
Fri Nov 30, 2018 6:58 pm
If only I had an assembler that supported AARCH64, sounds like a fun challenge, create a FAT binary for the ARM AARCH32 and AARCH64, should be interesting.
I hate to say it, but if you have Raspbian, the included assembler "as" happily supports AARCH64.
Harder to test though :(

User avatar
DavidS
Posts: 3698
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Assembly challange

Sat Dec 01, 2018 12:08 pm

The solution is simple, use a b ARM-32bit instruction to jump over the AARCH64 branch to entry (with a distance less than 16). On the instruction 0xEA00000n where n is the branch distance would be a ARM32 Branch, on AARCH64 it is a ANDS Rn, R0, R0 effective encoding (if I am reading the opcodes correctly for AARCH64), so benign.

So this is a simple challenge, even if I missread the opcodes there is a large enough difference between the two that finding the one that will branch on one of them should be a simple task for anyone that actually plays in AARCH64.
The Raspberry Pi is an ARM computer, that runs many Operating Systems, including Linux, RISC OS, BSD, Pi64 as well as many more.
Soon to add AROS, and TOSARM to the list of operating systems.

User avatar
DavidS
Posts: 3698
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Assembly challange

Sat Dec 01, 2018 12:11 pm

DavidS wrote: The solution is simple, use a b ARM-32bit instruction to jump over the AARCH64 branch to entry (with a distance less than 16). On the instruction 0xEA00000n where n is the branch distance would be a ARM32 Branch, on AARCH64 it is a ANDS Rn, R0, R0 effective encoding (if I am reading the opcodes correctly for AARCH64), so benign.

So this is a simple challenge, even if I missread the opcodes there is a large enough difference between the two that finding the one that will branch on one of them should be a simple task for anyone that actually plays in AARCH64.
jahboater wrote: I hate to say it, but if you have Raspbian, the included assembler "as" happily supports AARCH64.
Harder to test though
I do not use as which is actually gas because I do not like the syntax, personal pref.
The Raspberry Pi is an ARM computer, that runs many Operating Systems, including Linux, RISC OS, BSD, Pi64 as well as many more.
Soon to add AROS, and TOSARM to the list of operating systems.

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Sat Dec 01, 2018 1:11 pm

Yes, you are right, the point is to differentiate between AArch32 and AArch64 in run-time to have a fat binary.
And yes, @DavidS you're on the right track with the ANDS :-) And yes, this it isn't hard at all, I've deliberatly picked an easy task to involve more forum members.

There's no Assembler requirement, you can use anything you want. I wrote that ".long" (or "dd") directive is a solution too so that you can even use instructions not supported by a particular Assembler. You're welcome to use fasm-arm if you don't like gas syntax.
But you made your point, I'd like to clearify that the solution must contain which Assembler to use.

The winner post should contain code something like:

Code: Select all

; compile with (assembler)
_start:
   ? ? ?                               ; branch if executed as A64
   ... some A32 code ...
AArch64:
   ... some A64 code ...
As I've said, the opposite solution is equally correct.

Code: Select all

; compile with (assembler)
_start:
   ? ? ?                               ; branch if executed as A32
   ... some A64 code ...
AArch32:
   ... some A32 code ...
Hope you enjoy this challange,
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Sun Dec 02, 2018 9:45 am

1.) Formally you can read the CPU id for an ARM6 & ARM7 if it's either of those it has to be AARCH32.
2.) So then you come to an ARM8 It's still actually trivial the hint is look at the behaviour with an unknown opcode :-)

For example if GCC have done there job properly try

Code: Select all

void __builtin_trap (void);
If you need further help when you pull an exception look at PSTATE
http://infocenter.arm.com/help/index.js ... JGFCI.html

So you can easily identify an ARM8 is in AARCH32 or AARCH64 mode.

Put all you functions behind a function pointer and set the function pointers based on mode ... job complete.

I not only obscure the processor modes in that way with my SmartStart code I obscure the Pi model base address and all the different video modes for the console in the same way.

I can offer one bit of code you will need without doubt being this .. it allows an ARM6 thru but drops an ARM7, ARM8 AARCH32 out of hyp mode. The opcode are set by literal because an ARM6 assembler doesn't have a clue about those opcodes.

Code: Select all

mrs r0,cpsr		// Fetch the cpsr register which includes CPU mode bits 
and r1, r0, #0x1F		// Mask off the CPU mode bits to register r1                            
cmp r1, #CPU_HYPMODE	// Check we are in HYP_MODE											
bne .NotInHypMode	// Branch if not equal meaning was not in HYP_MODE  
bic r0,r0,#0x1F		// Clear the CPU mode bits in register r0							
orr r0, r0, #CPU_SVCMODE_VALUE// SVC_MODE bits onto register with Irq/Fiq disabled	
msr spsr_cxsf,r0			// Hold value in spsr_cxsf
add lr,pc,#4				// Calculate address of .NotInHypMode label
/* I borrowed this trick from Ultibo because ARM6 code running on an ARM7/8 needs this opcode  */
/* The ARM6 compiler does not know these instructions so it is a way to get needed opcode here */
/* So our ARM6 code can drop an arm7 or arm8 out of HYP mode and run on an ARM7/8.             */
/* Native ARM7/8 compilers already understand the OPCODE but do not mind it this way either	   */        
.long 0xE12EF30E		// "msr ELR_hyp, lr" Set the address to ELR_hyp
.long 0xE160006E		// "eret" Elevated return which will exit at .NotInHypMode in SVC_MODE
.NotInHypMode:

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Thu Dec 06, 2018 5:14 pm

LdB wrote:
Sun Dec 02, 2018 9:45 am
1.) Formally you can read the CPU id for an ARM6 & ARM7 if it's either of those it has to be AARCH32.
2.) So then you come to an ARM8 It's still actually trivial the hint is look at the behaviour with an unknown opcode :-)
So your solution is A32, but throws an UD abort when executed on A64, if I understand correctly?

This is not good, because you haven't set up the vbar_elX system registers. Now you need a code which does not abort on A32 but sets vbar on A64. Without that you branch to an undefined location. Otherwise good thinking!

Btw, __builtin_trap() is compiled into a BRK #0 instruction on A64 (haven't tried A32).

Cheers,
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Thu Dec 06, 2018 7:10 pm

Okay how about this .. you end up executing 32 bit code at 0x8000 even in AARCH64

Code: Select all

.data
.globl I_AM_64_BIT;
I_AM_64_BIT : .4byte 0;

Code: Select all

.org 0x8000
// If you start here you are AARCH32 from ARMSTUB.S, ARMSTUB7.S

// If I_AM_64_BIT = 1 ... you are actually now AARCH32 running under AAARCH64

// Execute your 32 bit code whatever you want

Code: Select all

.org 0x80000
AArch32_Mode_SVC     EQU		0x13
// if you start here you are AARCH64 EL2 from ARMSTUB8.S

// First set I_AM_64_BIT  to 1
MOV X0,  =I_AM_64_BIT 
MOV W1,  #1
STR W1, [X0]

// NOW SET EL1 to 32 bit
  MOV      x2, #1              ; NS bit
  MOV      x3, #(1 << 10)      ; RW bit

  MRS      x1, SCR_EL2
  BIC      w1, w1, w2          ; Clear NS bit (EL1 is secure)
  BIC      w1, w1, w3          ; Clear RW bit (EL1 is AArch32)
  MSR      SCR_EL2, x1

 // Now jump to 32 bit mode at normal start
  LDR      x1, =0x8000
  MSR      ELR_EL2, x1
  LDR      x1, =AArch32_Mode_SVC
  MSR      SPSR_EL2, x1
  ERET

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Fri Dec 07, 2018 8:30 pm

Don't get me wrong, your previous idea was not bad at all! I said "good thinking", because all I meant it was incomplete. (But could be a valid solution if start.elf clears vbar for sure (so it's always 0 after boot), because using kernel_old we can make start.elf to load our code at 0, meaning we can put a "b AArch64" in the right position.)
LdB wrote:
Thu Dec 06, 2018 7:10 pm
Okay how about this .. you end up executing 32 bit code at 0x8000 even in AARCH64
About this, this is an interesting idea too! Never considered the addresses 0x8000 and 0x80000 to be used for distintion before :-) Sounds promising, although I'm not sure how you suggest to put those two assembly in the same binary. Org is just an Assembler directive, it does not influence where start.elf loads our code (that only depends on the filename being kernel7.img or kernel8.img).

Cheers,
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Sat Dec 08, 2018 7:46 am

It is easy merge into a single file .. very common thing
http://infocenter.arm.com/help/index.js ... 15171.html

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Thu Dec 13, 2018 1:20 pm

Okay, but that's not what I meant. :-)

Let's assume we have a binary, how do you tell start.elf to load one part of it at 0x8000 when booted in A32 mode and another part to 0x80000 when booted in A64 mode? As far as I know the execution will start at the same first byte of the binary in both cases. (The obvious solution is to store one part in kernel7.img and the other in kernel8.img, but the challange is to have one binary which works regardless it's named kernel7.img or kernel8.img).

Btw, I'm looking for code that could determine it's own position in memory and jump depending on that. So far I have no luck, seems to be very difficult, but I haven't give up yet. :-)

Cheers,
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Thu Dec 13, 2018 2:55 pm

Ok for my solution you manually build the image always assuming 0x8000 and position 64 bit code at 0x80000
Then you force start address via config.txt with the line
kernel_address = 0x8000

You can also force 64bit if you need .. look for reference
https://www.raspberrypi.org/documentati ... xt/boot.md

I doubt you can do it by opcode as the new aarch64 opcode set is called a64.
search something like "a64 opcode bitfield" should give you the a64 layout
search something like "a32 opcode bitfield" should give you the a32 layout

I am guessing there is a set of zero opcodes that match as the encodings bitfields differ.
A64 also does not support thumb so my guess is your idea is dead in water.

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Fri Dec 14, 2018 10:09 pm

LdB wrote:
Thu Dec 13, 2018 2:55 pm
I doubt you can do it by opcode as the new aarch64 opcode set is called a64.
That's what gives the beauty of this challange, everybody doubts it is possible :-) But I can assure you, it's doable. I wouldn't made this challange if I weren't sure there's at least one good solution. That would be unfair, even evil to start a race without a finish line :-) And although DavidS hasn't presented a full assembly entry yet (hope he will), he already hinted a solution.

And thanks for the search tip, but I already have a fair knowledge on ARM instruction encoding. Enough to wrote an extremely compact (ca. 65k) disassembler for all the ARMv8.2 instructions ;-) But anyway, I appreciate your help! Thank you!
A64 also does not support thumb so my guess is your idea is dead in water.
Well, it doesn't matter whether A64 supports thumb or not, because the CPU is using only one of the modes at any given time. The challange is, can you create a binary that can be interpreted as A32, A64 and (optionally) in T32 mode as well, and passes control to different addresses in each mode?

Cheers,
bzt

LdB
Posts: 912
Joined: Wed Dec 07, 2016 2:29 pm

Re: Assembly challange

Sat Dec 15, 2018 4:10 am

hehe good work to you if it's doable.

It was one of those things my immediate reaction knowing the opcode bitfield patterns was there is no chance, and there was a more obvious way around it.

Be interested to see what you come up with, so don't forget to post it :-)

User avatar
DavidS
Posts: 3698
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Assembly challange

Sat Dec 15, 2018 1:34 pm

bzt wrote: That's what gives the beauty of this challange, everybody doubts it is possible :-) But I can assure you, it's doable. I wouldn't made this challange if I weren't sure there's at least one good solution. That would be unfair, even evil to start a race without a finish line :-) And although DavidS hasn't presented a full assembly entry yet (hope he will), he already hinted a solution.
I guess this means I actually have to submit something, without having an AARCH64 capable assembler. I already know it is easily doable, not as much of a challenge as the old 68K/x86/z80 CP/M 3 ISA FAT challenge of the early 1980's (that was a difficult one, and I do not remember the solution anymore), though in all three cases the executable was a flat binary having the first instruction at the start of the executable.

Ok I guess I will have to write something, and do the AARCH64 bit in AARCH64 machine language. 7th project added to the currently coding list.
The Raspberry Pi is an ARM computer, that runs many Operating Systems, including Linux, RISC OS, BSD, Pi64 as well as many more.
Soon to add AROS, and TOSARM to the list of operating systems.

bzt
Posts: 218
Joined: Sat Oct 14, 2017 9:57 pm

Re: Assembly challange

Sat Dec 15, 2018 6:30 pm

@LdB: actually I have already written that post: describing the whole process I've used to find the solution, for educational purposes. I'm just waiting a bit longer before I post it to give chance to others. I hope you'll find my post interesting :-)

@DavidS: no, no, you don't "have to". It would be great if you could send a post with a code block that fulfills these criteria, but I don't want you to think that's a must in any way (I'm already convinced you could do it, don't you worry :-)). If you don't do it happily for fun, the whole thing worths nothing! This is just a funny game, nothing more.

Btw the first similar challange I had (about twenty years ago or so) was to write a .COM file which would run as a DOS command file as well as a VMS DCL script. I had some sleepless nights, because DOS COM is a binary, but DCL COM is a script file with ASCII text only, but I have solved that puzzle! :-)

Cheers,
bzt

Return to “Bare metal, Assembly language”