what does # do after invokevirtual - java-bytecode-asm

For example if int-variables a and b are stored in local variables 1 and 2, this code would compute this.f(3+b*a). (this-pointer is stored in variable 0)
iload_1
aload_0
icons_3
iload_1
iload_1
imul
iadd
invokevirtual #4
for what does #4 stand here ?

This is an index into the constant pool of the classfile; it says that the operand of invokevirtual is stored at index #4 of the constant pool (which will be a Constant_MethodRef_info.) If you invoke javap with the -v flag, it will dump out the constant pool and you'll see at index #4 what class and method it is invoking.

Related

fp equal to sp at startup but when copied onto enlarged stack changes to zero - why?

I'm learning x32 ARM assembly on RaspberryPi with Raspbian. I wrote
the following code:
# Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified # modern syntax
.text
.align 2
.global main
.type main, %function
main:
mov r0, 1 # line added only for breakpoint purposes
sub sp, sp, 8 # space for fp, lr
str fp, [sp, 0] # save fp
str lr, [sp, 4] # and lr
add fp, sp, 4 # set our frame pointer
Build with gcc:
gcc -g test.s -o test
Use gdb to check values of fp and sp in lines 13 and 16 and
dereference them:
$ gdb ./test
(gdb) break 13
Breakpoint 3 at 0x103d4: file test.s, line 13.
(gdb) break 16
Breakpoint 4 at 0x103e0: file test.s, line 16.
(gdb) run
Starting program: /home/pi/assembly/nine/bob/test
Breakpoint 3, main () at test.s:13
13 sub sp, sp, 8 # space for fp, lr
(gdb) print {$sp, $fp}
$1 = {0x7efffae8, 0x7efffae8}
(gdb) x $sp
0x7efffae8: 0x76f9e000
(gdb) x $fp
0x7efffae8: 0x76f9e000
(gdb) continue
Continuing.
Breakpoint 4, main () at test.s:16
16 add fp, sp, 4 # set our frame pointer
(gdb) print {$sp, $fp}
$2 = {0x7efffae0, 0x7efffae0}
(gdb) x $sp
0x7efffae0: 0x00000000
(gdb) x $fp
0x7efffae0: 0x00000000
As you see fp is equal to sp at startup and non-zero:
(gdb) print {$sp, $fp}
$1 = {0x7efffae8, 0x7efffae8}
(gdb) x $sp
0x7efffae8: 0x76f9e000
(gdb) x $fp
0x7efffae8: 0x76f9e000
but when copied onto the enlarged stack it changes to zero
(gdb) x $fp
0x7efffae0: 0x00000000
Why does it change to zero? Why does it change value at all? Is
underlying implementation somehow linking values of fp and sp so
that when sp is moved down to the initialized memory that might be
all zeroes fp is changed as well? I only found
this:
fp
Is the frame pointer register. In the obsolete APCS variants that
use fp, this register contains either zero, or a pointer to the
most recently created stack backtrace data structure. As with the
stack pointer, the frame pointer must be preserved, but in
handwritten code it does not need to be available at every
instant. However, it must be valid whenever any strictly
conforming function is called. fp must always be preserved.
This
comment
says that lr is stored as the first element on the stack but it's
definitely not - it stays the same and is not zero:
(gdb) print {$sp, $fp, $lr}
$1 = {0x7efffae8, 0x7efffae8, 0x76e6b718 <__libc_start_main+268>}
and after sp changes:
(gdb) x/2xw $sp
0x7efffae0: 0x00000000 0x76e6b718
Ok, I'm answering myself - this happens in gdb/arm-tdep.c in GDB source code:
/* The frame size is just the distance from the frame register
to the original stack pointer. */
if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
{
/* Frame pointer is fp. */
framereg = ARM_FP_REGNUM;
framesize = -regs[ARM_FP_REGNUM].k;
}
else
{
/* Try the stack pointer... this is a bit desperate. */
framereg = ARM_SP_REGNUM;
framesize = -regs[ARM_SP_REGNUM].k;
}

Compare user-inputted string/character to another string/character

So I'm a bit of a beginner to ARM Assembly (assembly in general, too). Right now I'm writing a program and one of the biggest parts of it is that the user will need to type in a letter, and then I will compare that letter to some other pre-inputted letter to see if the user typed the same thing.
For instance, in my code I have
.balign 4 /* Forces the next data declaration to be on a 4 byte segment */
dime: .asciz "D\n"
at the top of the file and
addr_dime : .word dime
at the bottom of the file.
Also, based on what I've been reading online I put
.balign 4
inputChoice: .asciz "%d"
at the top of the file, and put
inputVal : .word 0
at the bottom of the file.
Near the middle of the file (just trust me that there is something wrong with this standalone code, and the rest of the file doesn't matter in this context) I have this block of code:
ldr r3, addr_dime
ldr r2, addr_inputChoice
cmp r2, r3 /*See if the user entered D*/
addeq r5, r5, #10 /*add 10 to the total if so*/
Which I THINK should load "D" into r3, load whatever String or character the user inputted into r2, and then add 10 to r5 if they are the same.
For some reason this doesn't work, and the r5, r5, #10 code only works if addne comes before it.
addr_dime : .word dime is poitlessly over-complicated. The address is already a link-time constant. Storing the address in memory (at another location which has its own address) doesn't help you at all, it just adds another layer of indirection. (Which is actually the source of your problem.)
Anyway, cmp doesn't dereference its register operands, so you're comparing pointers. If you single-step with a debugger, you'll see that the values in registers are pointers.
To load the single byte at dime, zero-extended into r3, do
ldrb r3, dime
Using ldr to do a 32-bit load would also get the \n byte, and a 32-bit comparison would have to match that too for eq to be true.
But this can only work if dime is close enough for a PC-relative addressing mode to fit; like most RISC machines, ARM can't use arbitrary absolute addresses because the instruction-width is fixed.
For the constant, the easiest way to avoid that is not to store it in memory in the first place. Use .equ dime, 'D' to define a numeric constant, then you can use
cmp r2, dime # compare with immediate operand
Or ldr r3, =dime to ask the assembler to get the constant into a register for you. You can do this with addresses, so you could do
ldr r2, =inputVal # r2 = &inputVal
ldrb r2, [r2] # load first byte of inputVal
This is the generic way to handle loading from static data that might be too far away for a PC-relative addressing mode.
You could avoid that by using a stack address (sub sp, #16 / mov r5, sp or something). Then you already have the address in a register.
This is exactly what a C compiler does:
char dime[4] = "D\n";
char input[4] = "xyz";
int foo(int start) {
if (dime[0] == input[0])
start += 10;
return start;
}
From ARM32 gcc6.3 on the Godbolt compiler explorer:
foo:
ldr r3, .L4 # load a pointer to the data section at dime / input
ldrb r2, [r3]
ldrb r3, [r3, #4]
cmp r2, r3
addeq r0, r0, #10
bx lr
.L4:
# gcc greated this "literal pool" next to the code
# holding a pointer it can use to access the data section,
# wherever the linker ends up putting it.
.word .LANCHOR0
.section .data
.p2align 2
### These are in a different section, near each other.
### On Godbolt, click the .text button to see full assembler directives.
.LANCHOR0: # actually defined with a .set directive, but same difference.
dime:
.ascii "D\012\000"
input:
.ascii "xyz\000"
Try changing the C to compare with a literal character instead of a global the compiler can't optimize into a constant, and see what you get.

Strings in java 8 less memory

I got below code and I was asked which option gets the following pattern:
XXXX-XXXX-XXXX-2324
...
Code Below:
public class CCMark {
public static String maskCC(String creditCard){
String x = "XXXX-XXXX-XXXX-";
//line 1
}
public static void main(String[] args) {
System.out.println(maskCC("1234-5678-1234-2324"));
System.out.println(maskCC("4567-5678-1234-5643"));
System.out.println(maskCC("1234-5678-1234-4654"));
System.out.println(maskCC("4567-5678-1234-5435"));
}
}
Below possible options that can be inserted on "line 1":
A)
return x + creditCard.substring(15, 19);
B)
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
return sb.toString();
I think that the best option here, as A and B provide us with the same output, is B, because it is using StringBuilder which means that its approach is mutable, so it will use less memory than option A.
Am I wrong? Could it be that option A for this particular situation is the best option?
Options a and b are identical, because the Java compiler will convert option a into option b. You could move the declaration of x outside the method (and make it final). Something like,
static final String x = "XXXX-XXXX-XXXX-";
public static String maskCC(final String creditCard) {
return x + creditCard.substring(15, 19);
}
Using javap to check the first against, the second. Java code like,
String x = "XXXX-XXXX-XXXX-";
String creditCard = "1234-5678-1234-23324";
String x2 = x + creditCard.substring(15, 19);
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
String x3 = sb.toString();
generates byte-code that looks like (note lines 6-31 and 32-58)
0: ldc #16 // String XXXX-XXXX-XXXX-
2: astore_1
3: ldc #18 // String 1234-5678-1234-23324
5: astore_2
6: new #20 // class java/lang/StringBuilder
9: dup
10: aload_1
11: invokestatic #22 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: bipush 15
20: bipush 19
22: invokevirtual #31 // Method java/lang/String.substring:(II)Ljava/lang/String;
25: invokevirtual #35 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_3
32: new #20 // class java/lang/StringBuilder
35: dup
36: aload_1
37: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: astore 4
42: aload 4
44: aload_2
45: bipush 15
47: bipush 19
49: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
52: pop
53: aload 4
55: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
58: astore 5
60: return
The big advantage of the variant A, return x + creditCard.substring(15, 19); is that it is simple and clean and it works in all Java versions from 1 to 8. In the case that its compiled form uses StringBuffer, a simple recompile for Java 5 or newer will make it use StringBuilder instead. This flexibility is lost when you work with either, StringBuffer or StringBuilder, manually.
The exact compiled form is not fixed. Since the semantic of the method String.substring is not fixed by the Java Language Specification, compilers usually won’t touch this and compile it as an ordinary method invocation. The specification encourages compiler vendors to use StringBuilder for string concatenation (the + operator) whenever there is a benefit and most compilers will do so, even when there is no benefit. Here, both, x and the result of substring, are Strings so a simple String.concat would be simpler but most compilers always use StringBuilder, compiling variant A to the equivalent of return new StringBuilder().append(x).append(creditCard.substring(15, 19)).toString();.
Comparing this typical form with your variant B, we can conclude that variant B has two advantages performance-wise:
new StringBuilder(x) initializes the StringBuilder to a capacity of x.length()+16 which is sufficient for the entire operation, whereas the default capacity of new StringBuilder(), typically used for variant A, is fixed to 16 characters which misses the mark here as we have a result of 19 characters, thus a reallocation and copying of the underlying character array will occur
sb.append(creditCard, 15, 19); will copy the four characters without the need to create an intermediate String representation of these characters. The expenses of the substring operation differ depending on the implementation, e.g. in Oracle’s implementation there was a significant change with version 1.7.0_06; starting with this version a substring requires a new char[] array holding a copy of the affected character data as it doesn’t maintain a separate offset and length field
But note that all these differences of variant A and B only affect the formal description of the operation to perform. What will actually happen, is up to the JVM/JRE and usually the Hotspot optimizer knows a lot of string related operations and may fuse operations or elide intermediate string representations. Thus, the outcome regarding performance is rather unpredictable and may be affected by subtle changes to the implementation.
That’s why developers might stick to variant A which is, as said, simpler and more readable, and only care for performance once a profiler tells them that there is a performance problem that could be solved by dealing with Stringbuilder manually.

Trouble With Reading In A String With A Subroutine In LC3

So I believe that the way I store the string works. I am just having some issues passing the String out of the subroutine. I heard that in order to pass something out of a subroutine you need to store it in R1 but I can't get it to store into my WORD array
.orig x3000
AND R1,R1,0
LEA R0,PROMPT
PUTS
JSR GETS
ST R1,WORD
LEA R0,WORD
PUTS
halt
; ---------Data Area-------------
WORD .blkw 20
PROMPT .stringz "Enter String: "
; -------------------------------
GETS LEA R1,MEMORYBLOCK ; saves the address of the storage memory block
loop GETC ; input character -> r0
PUTC ; r0 -> console
; always points at the next available block
LD R2,EMPTY ; check for
ADD R2,R2,R0 ; end of line
BRz finish
LD R2,COUNTDOWN
ADD R2,R2,#-1
BRz finish
ST R2,COUNTDOWN
STR R0,R1,#0 ; r0 -> ( memory address stored in r1 + 0 )
ADD R1,R1,#1 ; increments the memory pointer so that it
BR loop
finish LEA R1,MEMORYBLOCK
RET
; ----Subroutine Data Area-------
EMPTY .fill xfff6
COUNTDOWN .fill #10
MEMORYBLOCK .BLKW 20
; -------------------------------
.end
The biggest problem here is the concept of "returning a string". What you're actually doing at the end of GETS is returning the memory address at which the string starts. When you then store this into WORD in the calling function, you are storing the memory address of the first byte of the string that was input (i.e. the memory address of MEMORYBLOCK) into the first byte of WORD. You aren't copying the entire string from MEMORYBLOCK into WORD.
The easiest "fix" for what you're trying to do would be to change
LEA R0,WORD
to
LD R0,WORD
and then for good measure:
WORD .blkw 20
to
WORD .fill 0
as now you're just using it to store a single value (i.e. the memory address of MEMORYBLOCK).
However, at this point you haven't made a copy of the string. If you want to do this, then you will need to make a loop whereby you walk through MEMORYBLOCK and copy each byte to WORD instead.
The final, cheaper, way to do this is to just use MEMORYBLOCK directly from the calling function. It's not really any less valid in a program of this size, unless there's project requirements that ask otherwise.

Arglist differs from frame address

My program failed with segfault attempting to write "1" to a string.
(gdb) info frame
Stack level 0, frame at 0xb6b3c040:
eip = 0xb7877cdf; saved eip 0xb7858eae
called by frame at 0xb6b3cc50
Arglist at 0x91a1649, args:
Locals at 0x91a1649, Previous frame's sp is 0xb6b3c040
Saved registers:
ebx at 0xb6b3c02c, ebp at 0xb6b3c038, esi at 0xb6b3c030, edi at 0xb6b3c034, eip at 0xb6b3c03c
(gdb) bt
#0 0xb7877cdf in ?? () from /lib/i386-linux-gnu/libc.so.6
#1 0xb7858eae in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2 0xb787d91b in vsnprintf () from /lib/i386-linux-gnu/libc.so.6
#3 0x08ea7d7e in __gnu_cxx::__to_xstring<std::string, char> (__convf=0x85a2a50 <vsnprintf#plt>, __n=16, __fmt=0x91a1649 "%u") at /usr/include/c++/4.7/ext/string_conversions.h:95
#4 0x08ea6452 in std::to_string (__val=1) at /usr/include/c++/4.7/bits/basic_string.h:2871
...
I noticed that according to gdb, Arglist is not in stack. How it could happen? As far as I know, there is one calling convention in *nix: arguments are pushed to stack, caller clears stack frame. I went up and down through backtrace and everywhere else arglist was in stack.
You could be crashing in an assembly language routine that does not follow standard calling conventions and/or have symbolic information available.
Likely, the core issue is higher up than frame 0 anyway.

Resources