2016-04-26 10:13:57 8 Comments
I write the assembly code that can be compiled:
as power.s -o power.o
there is on problem when I link the power.o object file:
ld power.o -o power
In order to run on the 64bit OS (Ubuntu 14.04), I added .code32
at the beginning of the power.s
file, however I still get error:
Segmentation fault (core dumped)
power.s
:
.code32
.section .data
.section .text
.global _start
_start:
pushl $3
pushl $2
call power
addl $8, %esp
pushl %eax
pushl $2
pushl $5
call power
addl $8, %esp
popl %ebx
addl %eax, %ebx
movl $1, %eax
int $0x80
.type power, @function
power:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl %ebx, -4(%ebp)
power_loop_start:
cmpl $1, %ecx
je end_power
movl -4(%ebp), %eax
imull %ebx, %eax
movl %eax, -4(%ebp)
decl %ecx
jmp power_loop_start
end_power:
movl -4(%ebp), %eax
movl %ebp, %esp
popl %ebp
ret
Related Questions
Sponsored Content
10 Answered Questions
[SOLVED] Replacing a 32-bit loop counter with 64-bit introduces crazy performance deviations with _mm_popcnt_u64 on Intel CPUs
- 2014-08-01 10:33:29
- gexicide
- 153473 View
- 1376 Score
- 10 Answer
- Tags: c++ performance assembly x86 compiler-optimization
0 Answered Questions
2 Answered Questions
[SOLVED] sys_execve system call from Assembly
- 2012-02-18 15:21:47
- Alex F
- 21904 View
- 4 Score
- 2 Answer
- Tags: linux assembly x86 system-calls
2 comments
@muodostus 2018-10-16 05:05:20
I'm learning x86 assembly (on 64-bit Ubuntu 18.04) and had a similar problem with the exact same example (it's from Programming From the Ground Up, in chapter 4 [http://savannah.nongnu.org/projects/pgubook/ ]).
After poking around I found the following two lines assembled and linked this:
These tell the computer that you're only working in 32-bit (despite the 64-bit architecture).
If you want to use
gdb debugging
, then use the assembler line:The .code32 seems to be unnecessary.
I haven't tried it your way, but the gnu assembler (gas) also seems okay with:
.globl start
# (that is, no 'a' in global).
Moreover, I'd suggest you probably want to keep the comments from the original code as it seems to be recommended to comment profusely in assembly. (Even if you are the only one to look at the code, it'll make it easier to figure out what you were doing if you look at it months or years later.)
It would be nice to know how to alter this to use the
64-bit R*X
andRBP
,RSP
registers though.@Peter Cordes 2018-10-16 05:43:07
Correct,
.code32
is useless here, as I explained in my answer. And yes,.globl
is the normal GAS directive..global
is an alias for it.@Peter Cordes 2016-04-27 21:57:16
TL:DR: use
gcc -m32
..code32
does not change the output file format, and that's what determines the mode your program will run in. It's up to you to not try to run 32bit code in 64bit mode..code32
is for assembling "foreign" machine code that you might want as data, or to export in a shared-memory segment. If that's not what you're doing, avoid it so you'll get build-time errors when you build a.S
in the wrong mode if it has anypush
orpop
instructions for example.Suggestion: use the
.S
extension for hand-written assembler. (gcc foo.S
will run it through the C preprocessor beforeas
, so you can#include
a header with syscall numbers, for example). Also, it distinguishes it from.s
compiler output (fromgcc foo.c -O3 -S
).To build 32bit binaries, use one of these commands
Documentation for
nostdlib
,-nostartfiles
, and-static
.Using libc functions from
_start
(see the end of this answer for an example)Some functions, like
malloc(3)
, or stdio functions includingprintf(3)
, depend on some global data being initialized (e.g.FILE *stdout
and the object it actually points to).gcc -nostartfiles
leaves out the CRT_start
boilerplate code, but still linkslibc
(dynamically, by default). On Linux, shared libraries can have initializer sections that are run by the dynamic linker when it loads them, before jumping to your_start
entry point. Sogcc -nostartfiles hello.S
still lets you callprintf
. For a dynamic executable, the kernel runs/lib/ld-linux.so.2
on it instead of running it directly (usereadelf -a
to see the "ELF interpreter" string in your binary). When your_start
eventually runs, not all the registers will be zeroed, because the dynamic linker ran code in your process.However,
gcc -nostartfiles -static hello.S
will link, but crash at runtime if you callprintf
or something without calling glibc's internal init functions. (see Michael Petch's comment).Of course you can put any combination of
.c
,.S
, and.o
files on the same command line to link them all into one executable. If you have any C, don't forget-Og -Wall -Wextra
: you don't want to be debugging your asm when the problem was something simple in the C that calls it that the compiler could have warned you about.Use
-v
to have gcc show you the commands it runs to assemble and link. To do it "manually":gcc -nostdlib -m32
is easier to remember and type than the two different options for as and ld (--32
and-m elf_i386
). Also, it works on all platforms, including ones where executable format isn't ELF. (But Linux examples won't work on OS X, because the system call numbers are different, or on Windows because it doesn't even use theint 0x80
ABI.)NASM/YASM
gcc can't handle NASM syntax. (
-masm=intel
is more like MASM than NASM syntax, where you needoffset symbol
to get the address as an immediate). And of course the directives are different (e.g..globl
vsglobal
).You can build with
nasm
oryasm
, then link the.o
withgcc
as above, orld
directly.I use a wrapper script to avoid the repetitive typing of the same filename with three different extensions. (nasm and yasm default to
file.asm
->file.o
, unlike GNU as's default output ofa.out
). Use this with-m32
to assemble and link 32bit ELF executables. Not all OSes use ELF, so this script is less portable than usinggcc -nostdlib -m32
to link would be..I prefer yasm for a few reasons, including that it defaults to making long-
nop
s instead of padding with many single-bytenop
s. That makes for messy disassembly output, as well as being slower if the nops ever run. (In NASM, you have to use thesmartalign
macro package.)Example: a program using libc functions from _start
Fails at run-time, because nothing calls the glibc init functions. (
__libc_init_first
,__dl_tls_setup
, and__libc_csu_init
in that order, according to Michael Petch's comment. Otherlibc
implementations exist, including MUSL which is designed for static linking and works without initialization calls.)You could also
gdb ./a.out
, and runb _start
,layout reg
,run
, and see what happens.If we'd used
_exit(0)
, or made thesys_exit
system call ourselves withint 0x80
, thewrite(2)
wouldn't have happened. With stdout redirected to a non-tty, it defaults to full-buffered (not line-buffered), so thewrite(2)
is only triggered by thefflush(3)
as part ofexit(3)
. Without redirection, callingprintf(3)
with a string containing newlines will flush immediately.Behaving differently depending on whether stdout is a terminal can be desirable, but only if you do it on purpose, not by mistake.
@Michael Petch 2016-04-27 22:25:32
When building with
-nostartfiles -static
it can be dangerous in many environments if the C runtime needs to be run ahead of time. For simple things you'll probably not have a problem, but even printf can become a problem. That is why if you intend to useglibc
statically, your code should call__libc_init_first
,__dl_tls_setup
,__libc_csu_init
to be called manually (in that order) at program startup. You can avoid this by using C libraries like MUSL which require no initialization before functions are called. They are designed for static linking@Michael Petch 2016-04-27 22:28:40
You can get away with dynamic linking of GLIBC because that shared object will have its initialization code called automatically by the dynamic linker which will do those initialization calls.
@Peter Cordes 2016-04-27 22:28:45
@MichaelPetch: I did mention that in the comment above that line, if you scroll to the right... I didn't want to clutter the answer, but maybe that should be more prominent. Oh right, maybe my comment is wrong, because it only works with dynamic linking and
-nostartfiles
. Anyway, gtg, bbl in a couple hours.@Peter Cordes 2016-04-27 22:30:28
Edit if you have any nice ideas for how to present that info, otherwise I will at some point.
@Michael Petch 2016-04-27 22:31:34
Take care. Coincidentally I discovered we shared an answer related to this previously stackoverflow.com/questions/35208824/… . I had forgotten about it, although it was in the context of reducing code size so I never mentioned the initialization sequence needed for static linking to work.
@Peter Cordes 2016-04-28 20:49:48
@MichaelPetch: Is this too much stuff in one answer? I think I kept the essential info up front before wandering off into big examples and stuff. The x86 tag wiki links this answer for building 32bit code on a 64bit machine, so I figured that sending a newbie to this question might clear up other confusions, too, as well as illustrate strace and ltrace, and at least mention gdb.