Entradas Recentes »

Abaixo segue um pequeno tutorial sobre ferramentas de programação (“assembler”) em linux. Achei o texto simples, direto e helpful então vale a pena compartilhar.

JT.

========================================================================
LINUX ASSEMBLER TUTORIAL

by

Robin Miyagi

@

http://www.geocities.com/SiliconValley/Ridge/2544/

========================================================================

start@: Thu Feb 03 02:14:37 UTC 2000

update: Fri Jul 30 23:52:23 UTC 2000

update: Fri Sep 15 22:39:17 UTC 2000 :

– This tutorial now explains Linux assembler in terms of the GNU
assembler `as’.

– Information about Binutils programs such as Objdump, and ld.
Discussion on Debugging and `gdb’ is added.

update: Thu Jan 11 20:13:06 UTC 2001 :

========================================================================

* Introduction
————————————————————————

When programming in assembler for Linux (or any other Unix variant
for that matter), it is important to remember that Linux is a
protected mode operating system (on i386 machines, Linux operates
the CPU in protected mode). This means that ordinary user mode
processes are not allowed to do certain things, such as access DMA,
or access IO ports. Writing Linux kernel modules on the other hand
(which operate in kernel mode), are allowed to access hardware
directly (Read the Assembler-HOWTO on my assembler page for more
information on this issue). User mode processes may access hardware
using device files. Device files actually access kernel modules
which access hardware directly. This file will be restricted to
user mode operation. See my pages on kernel module programming.

Please email me comments and suggestions regarding this tutorial at
penguin@dccnet.com .

* System Calls
————————————————————————

In programming in assembler for DOS you probably made use of
software interrupts, especially the int 0×21 functions which were
the DOS system calls. In Linux, system calls are made via int 0×80.
The sytem call number is passed via register EAX, and the parameters
to the system call are passed via the remaining registers. This
discussion only applies if there are no more than five parameters
passed to the system call. If there are more than 5 parameters.
The parameters must be located in memory (e.g. on the stack), and
EBX must contain the address of the beginning of the parameters.

If you would like a list of the system call numbers, look at the
contents of /usr/include/asm/unistd.h. If you would like
information about a specific system call (e.g. write ()), type `man
2 write’ at the prompt. Section 2 of the linux man pages covers
sytem calls.

If you look at the contents of /usr/include/asm/unistd.h, you will
see the following line near the top of the file;

#define __NR_write 4

This indicates that register EAX must be set to 4 in order to call
the write () system call. Now, if you execute the following
command;

$ man 2 write

you get the following function description (under the SYNOPSIS
heading).

ssize_t write(int fd, const void *buf, size_t count);

This indicates that ebx is equal to the file descriptor of the file
you want to write to, ecx is a pointer of the string you want to
write, and edx contains the length of the string. If there were 2
more parameters to this system call, they would be placed in esi,
and edi respectively.

How do I know the file discriptor for stdout is 1. If you look at
your /dev directory, you will notice that /dev/stdout is a symbolic
link that points to /proc/self/fd/1. Therefore stdout is file
descriptor 1.

I leave looking up the _exit system call as an exercise.

In linux, system calls are processed by the kernel.

* GNU Assembler
————————————————————————

On most Linux systems, you will usually find the GNU C compiler
(gcc). This compiler uses an assembler called `as’ as a back-end.
This means that the C compiler translates the C code into assembler,
which in turn is assembled by `as’ to an object file (*.o).

`As’ uses the AT&T syntax. Experienced intel syntax assembler
programmers find AT&T `really weird’. It is really no more or no
less difficult than intel syntax. I switched over to `as’ because
there is less ambiguity, works better with the standard GNU/Linux
programs such as gdb (supports the gstabs format), objdump (objdump
dissassembles code in `as’ syntax). In short, it is a standard
component of a GNU Linux system with programming tools installed. I
will explain debugging and objdump later in this tutorial.

If you would like more information about `as’ look in the info
documentation under as (e.g. type `info as’ at the shell prompt).
Also look in the info documentation on the Binutils package (this
package contains such programming tools as objdump, ld, etc.).

** GNU assembler v.s. Intel Syntax
————————————————————————

Since most assembler documentation for the i386 platform is written
using intel syntax, some comparison between the 2 formats is in
order. Here is a summarized list of the differences;

– In `as’ the source comes before the the destination, opposite to
the intel syntax.

– The opcodes are suffixed with a letter indicating the size of
the opperands (e.g. `l’ for dword, `w’ for word, `b’ for byte).

– Immediate values must be prefixed with a `$’, and registers must
be prefixed with a `%’.

– Effective addresses use the General syntax
DISP(BASE,INDEX,SCALE). A concrete example would be;

movl mem_location(%ebx,%ecx,4), %eax

Which is equivelent to the following in intel syntax;

mov eax, [eax + ecx*4 + mem_location]

Now for an example illustrating the difference (intel version in
comments);

movl %eax, %ebx # mov %ebx, %eax
movw $0x3c4a, %ax

Now for our little program;
————————————————————————
## hello-world.s

## by Robin Miyagi
## http://www.geocities.com/SiliconValley/Ridge/2544/

## Compile Instructions:
## ————————————————————-
## as -o hello-world.o hello-world.s
## ld -o hello-world -O0 hello-world.o

## This file is a basic demonstration of the GNU assembler,
## `as’.

## This program displays a friendly string on the screen using
## the write () system call
########################################################################
.section .data
hello:
.ascii “Hello, world!\n”
hello_len:
.long . – hello
########################################################################
.section .text
.globl _start

_start:
## display string using write () system call
xorl %ebx, %ebx # %ebx = 0
movl $4, %eax # write () system call
xorl %ebx, %ebx # %ebx = 0
incl %ebx # %ebx = 1, fd = stdout
leal hello, %ecx # %ecx —> hello
movl hello_len, %edx # %edx = count
int $0×80 # execute write () system call

## terminate program via _exit () system call
xorl %eax, %eax # %eax = 0
incl %eax # %eax = 1 system call _exit ()
xorl %ebx, %ebx # %ebx = 0 normal program return code
int $0×80 # execute system call _exit ()

————————————————————————

In the above program, notice the use of `#’ to start comments. `As’
also supports the `/* C comment *’ syntax. If you use the C comment
syntax, it works exactly the same as for C (multiple lines, as well
as inline commenting). I always use the `#’ comment syntax, as this
works better with emacs’ asm-mode. The double `##’ is allowed but
not neccessary (this is only because of a quirk of emacs asm-mode).

Notice the names of the sections .text, and .data. these are used
in ELF files to tell the linker where the code and data segments
are. There is also the .bss section to store uninitialized data.
It is only these sections that occupy memory durring program
execution.

* Accessing Command Line Arguments and Environment Variables

When an ELF executable starts running, the command line arguments
and environment variables are available on the stack. In assembler
this means that you may access these via the pointer stored in ESP
when the program starts execution. See the documentation on my
assembler programming page relating to the ELF binary format.

So how is this data arranged on the stack? Quite simple really.
The number of command line arguments (including the name of the
program) are stored as an integer at [esp]. Then, at [esp+4] a
pointer to the first command line argument (which is the name of the
program) is stored. If there were any additional command line
parameters, their pointers would be stored in [esp+8], [esp+12],
etc. After all the command line argument pointers, comes a NULL
pointer. After the NULL pointer are all the pointers to the
environment variables, and then finally a NULL pointer to indicate
the end of the environment variables have been reached.

A summary of the initial ELF stack is shown below;

(%esp) argc, count of arguments (integer)
4(%esp) char *argv (pointer to first command line argument)
… pointers to the rest of the command line arguments
?(%esp) NULL pointer
… pointers to environment variables
??(%esp) NULL pointer

Now for our little program;
————————————————————————
## stack-param.s ###############################################

## Robin Miyagi ################################################
## http://www.geocities.com/SiliconValley/Ridge/2544/ ##########

## This file shows how one can access command line parameters
## via the stack at process start up. This behavior is defined
## in the ELF specification.

## Compile Instructions:
## ————————————————————-
## as -o stack-param.o stack-param.s
## ld -O0 -o stack-param stack-param.o
########################################################################
.section .data

new_line_char:
.byte 0x0a
########################################################################
.section .text

.globl _start

.align 4
_start:
movl %esp, %ebp # store %esp in %ebp
again:
addl $4, %esp # %esp —> next parameter on stack
movl (%esp), %eax # move next parameter into %eax
testl %eax, %eax # %eax (parameter) == NULL pointer?
jz end_again # get out of loop if yes
call putstring # output parameter to stdout.
jmp again # repeat loop
end_again:
xorl %eax, %eax # %eax = 0
incl %eax # %eax = 1, system call _exit ()
xorl %ebx, %ebx # %ebx = 0, normal program exit.
int $0×80 # execute _exit () system call

## prints string to stdout
putstring: .type @function
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %ecx
xorl %edx, %edx
count_chars:
movb (%ecx,%edx,$1), %al
testb %al, %al
jz done_count_chars
incl %edx
jmp count_chars
done_count_chars:
movl $4, %eax
xorl %ebx, %ebx
incl %ebx
int $0×80
movl $4, %eax
leal new_line_char, %ecx
xorl %edx, %edx
incl %edx
int $0×80
movl %ebp, %esp
popl %ebp
ret

————————————————————————

* The Binutils Package
————————————————————————

Binutils stands for binary utilities, and includes a lot of tools
useful to programmers, especially durring debugging.

I will now address some of these utilities.

** Objdump
————————————————————————

Objdump diplays information about 1 or more object files. For
example, to see information about param-stack, type the following
command at shell prompt (be sure working directory contains
param-stack);

objdump -x param-stack | less

Since the information is likely to span more than one screen, the
output of objdump is piped to the standard input of the paging
command `less’. the option `-x’ tells objdump to display the
numeric information in hexadecimal. Here is the output of the above
command;

—————————————————————-
stack-param: file format elf32-i386
stack-param
architecture: i386, flags 0×00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0×08048074

Program Header:
LOAD off 0×00000000 vaddr 0×08048000 paddr 0×08048000 align 2**12
filesz 0x000000be memsz 0x000000be flags r-x
LOAD off 0x000000c0 vaddr 0x080490c0 paddr 0x080490c0 align 2**12
filesz 0×00000001 memsz 0×00000004 flags rw-

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000004a 08048074 08048074 00000074 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000001 080490c0 080490c0 000000c0 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 080490c4 080490c4 000000c4 2**2
ALLOC
SYMBOL TABLE:
08048074 l d .text 00000000
080490c0 l d .data 00000000
080490c4 l d .bss 00000000
00000000 l d *ABS* 00000000
00000000 l d *ABS* 00000000
00000000 l d *ABS* 00000000
080490c0 l .data 00000000 new_line_char
08048076 l .text 00000000 again
08048087 l .text 00000000 end_again
0804808e l .text 00000000 putstring
08048096 l .text 00000000 count_chars
080480a0 l .text 00000000 done_count_chars
00000000 F *UND* 00000000
080480be g O *ABS* 00000000 _etext
08048074 g .text 00000000 _start
080490c1 g O *ABS* 00000000 __bss_start
080490c1 g O *ABS* 00000000 _edata
080490c4 g O *ABS* 00000000 _end

—————————————————————-

Notice the Information provided from the program header (ELF files
have header information at the beginning of the file giving
information to the kernel on how to load the file into memory etc.).

ELF files also contain information about the sections (contained in
section tables). Notice that the .text section contains 0x4a bytes
of information, is located 0×74 bytes into the file, and is aligned
at a 4 byte boundary (4 == 2 ** 2), has memory allocated to it
(ALLOC), is readoly, and contains code (the segment selector cs for
this process points to this section (handled by the operating
system)).

Information about the symbols is also provided. All this
information is used by debuggers and other programming tools to
examine binary files.

Objdump can also be used to dissasemble binary executables. Typeing
the following command will dissassemble the file to standard output
(this does nothing to the actual file, as objdump only reads from
the file);

objdump -d stack-param | less

Here is the output of the above command;

—————————————————————-
stack-param: file format elf32-i386

Disassembly of section .text:

08048074 :
8048074: 89 e5 movl %esp,%ebp

08048076 :
8048076: 83 c4 04 addl $0×4,%esp
8048079: 8b 04 24 movl (%esp,1),%eax
804807c: 85 c0 testl %eax,%eax
804807e: 74 07 je 8048087
8048080: e8 09 00 00 00 call 804808e
8048085: eb ef jmp 8048076

08048087 :
8048087: 31 c0 xorl %eax,%eax
8048089: 40 incl %eax
804808a: 31 db xorl %ebx,%ebx
804808c: cd 80 int $0×80

0804808e :
804808e: 55 pushl %ebp
804808f: 89 e5 movl %esp,%ebp
8048091: 8b 4d 08 movl 0×8(%ebp),%ecx
8048094: 31 d2 xorl %edx,%edx

08048096 :
8048096: 8a 04 11 movb (%ecx,%edx,1),%al
8048099: 84 c0 testb %al,%al
804809b: 74 03 je 80480a0
804809d: 42 incl %edx
804809e: eb f6 jmp 8048096

080480a0 :
80480a0: b8 04 00 00 00 movl $0×4,%eax
80480a5: 31 db xorl %ebx,%ebx
80480a7: 43 incl %ebx
80480a8: cd 80 int $0×80
80480aa: b8 04 00 00 00 movl $0×4,%eax
80480af: 8d 0d c0 90 04 08 leal 0x80490c0,%ecx
80480b5: 31 d2 xorl %edx,%edx
80480b7: 42 incl %edx
80480b8: cd 80 int $0×80
80480ba: 89 ec movl %ebp,%esp
80480bc: 5d popl %ebp
80480bd: c3 ret
—————————————————————-

The `-d’ tells objdump to disassemble sections that are expected to
contain code (usually the .text section). Using the `-D’ option
will disassemble all sections. Objdump was able to give the names
of labels in the code because of the information contained in the
symbols table.

The first column displays the virtual memory address for each line
of code. The second column displays the machine code corresponding
to its respective assembler line of code, and finally the code in
assembler is contained in the 3rd column.

For more information look in the info documentation system.

** Getting the amount of memory used with size
————————————————————————

If you do an `ls -l stack-param’ you get the following

-rwxrwxr-x 1 robin robin 932 Sep 15 18:21 stack-param

This tells you that the file is 932 bytes long. However this file
also contains header tables, section tables, symbol tables etc. The
amount of memory that this program will use durring run time will be
less than this. To find out actual memory use, type the following;

size stack-param

The above will result in the following output;

text data bss dec hex filename
74 1 0 75 4b stack-param

This tells you that .text occupies 74 bytes, and .data occupies one
byte, for a total of 75 bytes memory use.

** Getting rid of symbol information with strip
————————————————————————

The strip command can be used to get rid of the symbol information.
With no options, this command only strips symbols that are not used
for debugging. With the `–stip-all’ option provided, it will strip
all symbol information, including those used for debugging. I
recommend not doing this, as this makes the files harder to analyse
with the standard programming tools. This command is used only if
file size is of paramount importance.

* debugging and gdb
————————————————————————

Perhaps the most difficult aspect of programming is debugging.
Quite often the error that caused the program to terminate
abnormally is not at the line where the program terminated (the
example later on will show this).

Program that exits with SIG_SEGV
————————————————————————
## stack-param-error.s #########################################

## Robin Miyagi ################################################
## http://www.geocities.com/SiliconValley/Ridge/2544/ ##########

## This file shows how one can access command line parameters
## via the stack at process start up. This behavior is defined
## in the ELF specification.

## Compile Instructions:
## ————————————————————-
## as –gstabs -o stack-param-error.o stack-param-error.s
## ld -O0 -o stack-param-error stack-param-error.o
########################################################################
.section .data

new_line_char:
.byte 0x0a
########################################################################
.section .text

.globl _start

.align 4
_start:
movl %esp, %ebp # store %esp in %ebp
again:
addl $4, %esp # %esp —> next parameter on stack
leal (%esp), %eax # move next parameter into %eax
testl %eax, %eax # %eax (parameter) == NULL pointer?
jz end_again # get out of loop if yes
call putstring # output parameter to stdout.
jmp again # repeat loop
end_again:
xorl %eax, %eax # %eax = 0
incl %eax # %eax = 1, system call _exit ()
xorl %ebx, %ebx # %ebx = 0, normal program exit.
int $0×80 # execute _exit () system call

## prints string to stdout
putstring: .type @function
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %ecx
xorl %edx, %edx
count_chars:
movb (%ecx,%edx,$1), %al
testb %al, %al
jz done_count_chars
incl %edx
jmp count_chars
done_count_chars:
movl $4, %eax
xorl %ebx, %ebx
incl %ebx
int $0×80
movl $4, %eax
leal new_line_char, %ecx
xorl %edx, %edx
incl %edx
int $0×80
movl %ebp, %esp
popl %ebp
ret
————————————————————————

Notice that the above program is assembled with the `–gstabs’
option of `as’. This make as put debugging information in output
file, such as the original source file, debugging symbols etc.
Using `objdump -x stack-param-error | less’ will show you the
inclusion of debugging symbols.

Now to find out where our error occurred type the following command;

gdb stack-param-error

this will get you to the gdb prompt `(gdb)’;

(gdb) run eat my shorts
/home/robin/programming/asm-tut/stack-param-error
eat
my
shorts
Program recieved SIGSEGV, segmentation fault
count_chars () at stack-param-error.s:47

47 movb (%ecx,%edx,$1), %al
Current language: auto; currently asm
(gdb) q
[~]$ _

(gdb will output more than this, I just wanted to highlight what
is important).

This tells us that the segmentation fault occured at line 47 of
param-stack-error.s. However the problem was caused in line 29. If
you look at line 29 of stack-param.s, you will see that this line
reads `movl (%esp), %eax’. This is due to the way intel i386 opcode
lea handles NULL pointers. EAX was never loaded with 0 on a null
pointer (just some invalid pointer), which caused line 47 to access
an area of memory not available to this process (hence the
segmentation fault). The loop in _start () never stopped normally,
as the condition for breaking out of the loop is eax being 0, which
never happened.

Debugging is an art that comes with practice. For more information
about gdb, look in the info pages (e.g. `info gdb’). You can also
type `help’ at the (gdb) prompt.

The only reason gdb was able to tell you what line number in the
source code the error occured is that the debugging symbols and
source code was included in the output file (recall that we used the
`–gstabs’ option).

——————————————————————–
Comments and suggestions

========================================================================

You are free to make verbatim copies of this file, providing that this
notice is preserved.

por Jáder vanderlei Muniz de Souza

            A leitura é basicamente um processo de representação. Como esse processo envolve o sentido da visão, ler é, na sua essência, olhar para uma coisa e ver outra. A leitura não se dá por acesso direto à realidade, mas por intermediação de outros elementos da realidade. Nessa triangulação da leitura o elemento intermediário funciona como um espelho; mostra um segmento do mundo que normalmente nada tem a ver com sua consistência física. Ler é, portanto, reconhecer o mundo através de espelhos. Como esses espelhos oferecem imagens fragmentadas do mundo, a verdadeira leitura só é possível quando se tem um conhecimento prévio desse mundo.

Embora a leitura, na concepção mais comum do termo, processa-se através da língua, também é possível a leitura através de sinais não-linguísticos. Pode-se ler tristeza nos olhos de alguém, a sorte não mão de uma pessoa ou o passado de um povo nas ruínas de uma cidade. Não se lê, portanto, apenas a palavra escrita mas também o próprio mundo que nos cerca.

O processo de triangulação, no entanto, é o mesmo. Ao fazermos a leitura sociológica de uma rua da cidade olhamos para as casas, o calçamento, as pessoas, mas vemos a realidade sociológica refletida por essa rua.

O segundo elemento da realidade não está em relação unívoca com o primeiro. Sendo o primeiro elemento um espelho, a visão a ser dada por esse espelho, depende da posição da pessoa em relação ao espelho. Diferentes posições refletem diferentes segmentos da realidade. Numa leitura do mundo, o objeto para o qual se olha funciona como um espelho. Se o objeto for, por exemplo, uma casa, vai oferecer tantas leituras quantas forem as posições de cada um dos observadores em relação à casa. O arquiteto fará uma leitura arquitetônica, o sociólogo uma leitura sociológica, o ladrão uma leitura estratégica, e assim por diante.

Sem triangulação não há leitura. Às vezes, no entanto, a triangulação não é possível. Quando o leitor diz “li mas não entendi”, ele ficou apenas no primeiro elemento da realidade; olhou mas não viu. Ouve tentativa de leitura mas não ouve leitura.

Entre o leitor e o que ele vê através da leitura pode haver mais de um espelho. Ocorre então que aquilo que é percebido é um reflexo do reflexo da realidade. Esse parece ser principalmente o caso da leitura de uma obra literária, que pode implicar não apenas reflexos de reflexos mas verdadeiro encadeamentos de reflexos. Na leitura de um poema, por exemplo, um determinado segmento da realidade (um dos possíveis significados do poema) pode ser refletido através de vários espelhos até chegar à percepção do leitor.

Primordialmente, na sua concepção mais geral e fundamental, ler é usar segmentos da realidade para chegar a outros segmentos. Dentro dessa acepção, tanto a palavra escrita como outros objetos podem ser lidos, desde que sirvam como elementos intermediários, indicadores de outros elementos. Esse processo de triangulação, de acesso indireto à realidade, é a condição básica para que o ato da leitura ocorra.

 

  • Perspectivas de leitura:

ü  Leitura como extração de significado do texto

Um dos axiomas da leitura é de que ler implica significado, sendo significado aquele segmento da realidade a que se chega através de um outro segmento. O significado pode estar em vários lugares, mas ao se usar o verbo extrair, põe-se o significado dentro do texto. Uma analogia que parece refletir adequadamente esta acepção de leitura é a de que o texto é uma mina, possivelmente com inúmeros corredores subterrâneos, cheia de riquezas, mas que precisa ser persistentemente explorada pelo leitor.

Essa leitura extração-de-significado está associada a idéia de que o texto tem um significado preciso, exato e completo, que o leitor-minerador pode obter através do esforço e da persistência. Como o texto contém o significado, esse texto precisa ser apreendido pelo leitor na sua íntegra. A leitura deve ser cuidadosa, com consulta ao dicionário sempre que uma palavra desconhecida for encontrada e anotação da palavra para revisões posteriores e enriquecimento do vocabulário. Frases de compreensão difícil devem ser lidas e relidas até que a compreensão fique clara.

A adivinhação de palavras novas pelo contexto deve ser evitada porque a leitura é um processo exato e a compreensão não comporta aproximações. O texto está cheio de armadilhas para o leitor impulsivo que não sabe parar e refletir diante dos vocábulos que só são semelhantes na aparência ou de figuras de linguagem que precisam ser reconhecidas para que se possa apreciar a beleza do texto. Tudo que o texto contém precisa ser detectado e analisado para que o seu verdadeiro significado possa ser extraído.

Erros de leitura oral são vistos como provas de deficiência em leitura. A leitura é um processo linear que se desenvolve palavra por palavra. O significado é extraído – vai-se acumulando – à medida em que essas palavras vão sendo processadas.

O aspecto visual da leitura – o papel dos olhos – é de extrema importância nesta acepção de leitura. O significado vai do texto ao leitor, através dos olhos. Nenhuma palavra é entendida antes de ser vista. O raciocínio do leitor é comandado pela informação que entra pelos olhos.

O leitor está subordinado ao texto, que é o pólo mais importante da leitura. Se o texto for rico, o leitor se enriquecerá com ele, aumentará seu conhecimento de tudo porque o texto é mundo. Se o texto for pobre, mina sem ouro, o leitor perderá seu tempo, porque nada há para extrair.

O leitor-minerador tem no entanto muito a ganhar, porque há uma riqueza incalculável nos livros tudo que ele de melhor produziu o pensamento humano está registrado na permanência da palavra escrita.

A compreensão é o resultado do ato da leitura. O valor da leitura só pode ser medido depois que a leitura terminou. A ênfase não está no processe da compreensão, na construção do significado, mas no produto final dessa compreensão.

A leitura é um processe ascendente. A compreensão sobe do texto ao leitor na medida exata em que o leitor vai avançando no texto. As letras vão formando palavras, as palavras frases e as frases parágrafos. O texto é processa do literalmente da esquerda para a direita e de cima para baixo.

A concepção da leitura como um processo de extração tem, no entanto, sérias limitações. O verbo extrair, em primeiro lugar, não reflete o que  realmente acontece na leitura. O leitor não extrai um conteúdo do texto, como se o texto fosse uma mina que se esvazia com a mineração. O conteúdo não se transfere do texto para o leitor, mas antes se reproduz no leitor, sem deixar de permanecer no texto. Conceptualmente, não teríamos portanto uma extração, mas uma cópia.

Na realidade, o texto não possui um conteúdo mas reflete-o como um espelho. Assim como não há qualquer identidade física entre o material de que é feito o espelho e o material que ele reflete, não existe também uma relação unívoca entre o texto e o conteúdo. Um mesmo texto pode refletir vários conteúdos, como vários textos podem também refletir um só conteúdo.

 

 

ü  Leitura com atribuição de significado ao texto

            A acepção de que ler é atribuir significado, põe a origem do significado não no texto mas no leitor. O mesmo texto pode provocar em cada leitor e mesmo em cada leitura uma visão diferente da realidade.

A visão da realidade provocada pela presença do texto depende da bagagem de experiências prévias que o leitor traz para a leitura. O texto não contém a realidade, reflete apenas segmentos da realidade, entremeados de inúmeras lacunas, que o leitor vai preenchendo com o conhecimento prévio que possui do mundo.

A qualidade do ato da leitura não é medida pela qualidade intrínseca do texto, mas pela qualidade da reação do leitor. A riqueza da leitura não está necessariamente nas grandes obras clássicas, mas na experiência do leitor ao processar o texto. O significado não está na mensagem do texto mas na série de acontecimentos que o texto desencadeia na mente do leitor.

Ler não implica necessariamente apreender a mensagem na sua íntegra. A leitura pode ser lenta e cuidadosa como rápida e superficial, com ou sem consulta ao dicionário. A adivinhação de palavras desconhecidas pelo contexto é incentivada. Ao encontrar uma frase de com preensão difícil, o leitor não deve parar e reler, mas ler adiante; provavelmente entendendo a frase ao chegar ao fim do parágrafo.

Erros de leitura oral são interpretados do ponto de vista qualitativo e considerados apenas como desvios. Não importa cometer muitos erros; o que imteressa é o tipo de erro cometido. Se no texto, por exemplo, estiver escrito “gatinho” e o leitor ler “bichinho” mantendo a coerência interpretativa, considera-se que a qualidade da leitura não é prejudicada.

A leitura não é interpretada como um procedimento linear, onde o significado é construído palavra por palavra, mas como um procedimento de levantamento de hipóteses. O que o leitor processa da página escrita é o mínimo necessário para confirmar ou rejeitar hipóteses.

Os olhos não vêem o que realmente está escrito na pagina, mas apenas determinadas informações pedidas pelo cérebro. A compreensão não começa pelo que esta na frente dos olhos, mas pelo que esta atrás deles. A palavra “nós”, por exemplo, poderá ser entendida como plural de “nó” ou como pronome pessoal, dependendo do que o cérebro mandou o olho buscar, baseado naturalmente no contexto em que se encontra a palavra.

A compreensão não é um produto final, acabado, mas um processo que se desenvolve no momento em que a leitura é realizada. A ênfase não está na dimensão espacial e permanente do texto mas no aspecto temporal e mutável do ato da leitura. O interesse do pesquisador ou do professor não está no produto final da leitura, na compreensão extraída do texto, mas principalmente em como se dá essa compreensão, que estratégias, que recursos, que voltas o leitor dá para atribuir um significado ao texto.

A leitura é um processo descendente; desce do leitor ao texto. A compreensão começa com o estabelecimento do tópico, sugerido no primeiro contato com o texto, ainda em termos gerais. Usando os traços mais salientes da pagina a ser lida – título, gráficos, ilustrações, nome do autor, etc. – o leitor levanta uma série de hipóteses e começa a testá-las, desde o nível do discurso até o nível grafofonêmico, passando pelos níveis sintático e lexicais.

A acepção da leitura como um ato de atribuição de significado também tem seus problemas. Teoricamente, parece haver um paradoxo quanto à quantidade de informação fornecida pelo texto, que pode ser a mais ou menos, mas dificilmente na quantidade certa.

Há informação a mais quando o texto parece oferecer mais do que o leitor precisa. Diz-se que o texto é redundante. Ler com eficiência neste caso é saber explorar a redundância do texto, processando apenas a informação necessária para confirmar ou rejeitar as hipóteses inicialmente levantadas.

Há informação a menos quando o texto é visto como uma seqüência de lacunas. Existe muito conhecimento comum entre o escritor e o leitor, e o escritor capitaliza em cima desse conhecimento no momento em que produz o texto, deixando muita coisa para ser preenchida pelo leitor. Ler é neste caso preencher essas lacunas deixadas pelo escritor.

Dentro dessa mesma concepção de leitura com atribuição de significado há portanto duas concepções antagônicas de texto. Há os que vêem o texto como uma fonte de redundâncias e os que o percebem cheio de lacunas. A cada uma dessas visões corresponde também uma visão diferente de leitura: um processo altamente seletivo quando a informação é redundante e extremamente construtivo quando a informação é truncada. Em ambos os casos o papel do leitor no entanto é mais ou menos o mesmo. Quer ele use apenas parte da informação fornecida pelo texto, quer ele preencha as lacunas deixadas pelo mesmo, a obtenção do significado se dá sempre por força de sua contribuição. Num caso o leitor contribui com aquilo que o texto não tem; no outro com aquilo que o texto já tem, preferindo no entanto usar sua contribuição pessoal em vez da informação redundante do texto.

O pressuposto de que o mesmo texto pode proporcionar uma leitura diferente em cada leitor e até de que o mesmo leitor não fará leituras idênticas de um mesmo texto, tem também levantado alguns problemas. Ainda que toda experiência com o texto que remete o leitor de algum modo a um determinado seguimento da realidade seja em principio limitar as possíveis interpretações de um determinado texto. Se alguém interpreta um poema satírico ao pé da letra, não deixa essencialmente de realizar um ato de leitura, de atribuir um significado ao texto, mas deixou de perceber que o que estava sendo refletido pelo texto não era a realidade, mas um reflexo do reflexo da realidade.

A ênfase na construção de sentido a partir do leitor pode exigir portanto que se defina o perfil desse leitor, em termos mais ou menos ideais. Nesse caso, pode executar o ato da leitura, o leitor precisa conhecer o jogo de espelhos que se interpõe entre ele e a realidade. Podemos dizer que o leitor precisa possuir, além da competência sintática, semântica e textual, uma competência específica da realidade histórico-social refletida pelo texto.

 

ü  Uma visão conciliatória

            Ao definirmos a leitura quer como um processo de extração de significado (ênfase no texto) quer como um processo de atribuição de significado (ênfase no leitor) encontramos, em ambos os casos, uma série de problemas mais ou menos intransponíveis. A complexidade do processo da leitura não permite que se fixe em apenas um de seus pólos, com exclusão do outro. Na verdade, não basta nem mesmo somar as contribuições do leitor e do texto. É preciso considerar também um terceiro elemento: o que acontece quando leitor e texto se encontram. Para compreender o ato da leitura temos que considerar então (a) o papel do leitor, (b) o papel do texto e (c) o processo de interação entre o leitor e o texto.

Para melhor explicar esse processo de interação entre leitor e texto, vamos fazer uma analogia entre o processo da leitura e uma reação química. Na leitura, como na química, para termos uma reação é necessário levar em conta não só os elementos envolvidos, mas também as condições necessárias para que a reação ocorra. O simples confronto do leitor com o texto não garante a eclosão de todos os acontecimentos que caracterizam o ato da leitura. A produção de uma nova substancia – no caso a compreensão – só ocorre se houver afinidade entre os elementos leitor e texto e se determinadas condições estiverem presentes.

O leitor precisa possuir, além das competências fundamentais para o ato da leitura, a intenção de ler. Essa intenção pode ser caracterizada como uma necessidade que precisa ser satisfeita, a busca de um equilíbrio interno ou a tentativa de colimação de um determinado objetivo em relação a um determinado texto.

Essa intencionalidade é característica exclusiva do ser humano. Uma maquina pode ser programada para resumir ou parafrasear um texto, detectar anomalias semânticas e até responder perguntas implícitas; seria difícil, no entanto, imaginar uma maquina que, espontaneamente, ficasse horas entretida com a leitura de um grande romance. A máquina não teria a intenção do lazer, como não teria intenção de obter informações da bolsa de valores ou de fazer uma leitura critica de um poema de Mallarmé.

Satisfeita essa condição básica de intencionalidade, inicia-se o processo complexo de interação entre o leito e o texto. A leitura é um processo feito de muitos processos, que ocorrem tanto simultânea como seqüencialmente; esses processos incluem desde habilidades de baixo nível, executadas de modo automático na leitura proficiente, até estratégias de alto nível, executadas de modo consciente.

O processo da leitura fluente pode ser representado por uma pirâmide, em cuja base estão as habilidades elementares, envolvendo subprocessos que ocorrem em grandes feixes, de modo rápido, simultâneo e abaixo do nível da consciência. Como esse processo ocorrem em feixes, fala-se, nesse nível de leitura, de um processamento em paralelo.

A leitura, mecanicamente, dá-se por fixações dos olhos em determinados segmentos do texto, que podem ser uma palavra ou um pequeno grupo de palavras. Ao que parece o leitor não processa a letra que compõem um determinado segmento de modo linear, da esquerda para a direita, mas de modo simultâneo. Também parece que as letras não são processadas integralmente, em todos os detalhes, mas apenas nos traços distintivos. O leito não tem na memória um molde para cada letra do alfabeto. Uma leitura feita pelo cotejo de cada letra com esse molde fixo seria extremamente complicada e ante econômica, já que seria necessário não um molde para cada letra do alfabeto, mas para cada tipo possível de letra (maiúscula, minúscula, negrito, itálico, todos os diferentes tipo usados em diferentes maquinas tipográficas e de escrever, sem falar nas diferentes caligrafias de cada pessoa).

ü  O leitor-aluno

Para realizar uma leitura profunda, atribuindo o máximo de sentido possível ao texto, é necessário possua não a intenção de ler, como outras habilidades, indispensáveis ao processo de relação com o texto. Ler não é apenas decodificar, mas pressupõe uma perfeita decodificação, além de capacidade de inferência, utilizando conhecimentos de mundo e toda sorte de conhecimentos prévios, na contextualização do tema abordado.

A boa formação de um leitor implica no desenvolvimento das habilidades citadas, ligadas incondicionalmente ao hábito da leitura, não apenas como atividade em sala de aula ou em tarefas relacionadas, mas como parte integrante do dia-a-dia do leitor-aluno. Entretanto a escola tem papel preponderante nesse aspecto, devendo contribuir para que esse hábito se desenvolva.

 

Fonte: http://www.unicaieiras.com.br/revista/artigos.html

Git Tutorial

Olá pessoal!

Digamos que você está escrevendo sua monografia/dissertação/tese/artigo, etc.. claro que você não quer correr o risco de perde-la não é mesmo? O que você faz? Backup! Mas aposto que você já teve também a vontade de restaurar o seu trabalho para uma estrutura/conteúdo que você tinha há algum tempo atrás ou quem sabe recuperar aquele texto que você sobrescreveu… Que tal resolver os dois problemas de uma vez só?

Recentemente tive a idéia de utilizar um sistema para controle de versão para fazer esse trabalho dos meus “documentos”, em geral códigos e textos, não demorou muito para eu descobrir que alguns colegas do lab já usavam isso há algum tempo… O.o Eu já conhecia o Svn, más comecei a utilizar o Git porquê já existe uma infraestrutura aqui suportando o Git, a primeira vista me parece bem seguro (é distribuído) e prático, além do mais diversos projetos open source o utilizam. Vale a pena dar uma olhada. Segue abaixo um tuto que gostei.

Abs.
John.

 

Git is a distributed version control system (dvcs) written in C. A version control system allows the creation of a history for a collection of files and includes the functionality to revert the collection of files to another state. Another state might be a different collection of files or different content in the files.

You may, for example, change the collection of files to a state from 2 days ago or you may switch between states for experimental features and production issues.

The collection of files is usually called “source code”. In a distributed version control system everyone has a complete copy of the source code (including the complete history of the source code) and can perform version control operations against this local copy. The use of a dvcs does not require a central code repository.

If you make changes to the source code you mark them as relevant for the version control (add them to the index / staging) and then add them to the repository (commit).

Git maintains all versions. Therefore you can revert to any point in your source code history using Git.

Git performs commits to your local repository and you can synchronize your repository with other (remote) repositories. Git allows you to clone repositories, e.g. create an exact copy of a repository including the complete history of the source code. Owners of repositories can synchronize changes via push (transferring changes to a remote repository) or pull (getting changes from a remote repository).

Git supports branching, e.g. you can have different versions of your source code. If you want to develop a new feature, you may open a branch in your source code and make the changes in this branch without affecting the main line of your code.

Continue reading at Vogella.de

The original post of this text is http://www.brainycreatures.org/compiler/sablecc.asp

To create a compiler with SableCC we follow the following steps:

  1. We create a SableCC specification file containing the lexical definitions and the grammar for the language being designed.
  2. After creating the SableCC specification file, we generate the framework by launching SableCC on the specification file. .
  3. At this stage, we generate the working classes. This is the only code we have to write in JavaTM. It is in this step that we write the semantic analyzer, the code generator, and possibly the code optimizer. In the case of an interpreter, we write a single class. These working classes may be subclasses of one of the classes from the analysis subfolder generated by SableCC in the previous step.
  4. It is during this step that we create the driver class of the compiler. It is used to activate the lexer, parser and working classes.
  5. Finally, in this step, we compile the compiler with a JavaTM compiler.

To illustrate this, we are going to implement a subset of Pascal by following the steps outlined above. We are going to implement a subset instead of the full language, because we want to give the reader an idea of how SableCC works.

We are now going to start the implementation

SableCC specification file

Before we start implementing the SableCC specification file for the subset of Pascal, we are going to describe the syntax of SableCC using BNF. I am assuming that you have read the introduction to regular expressions and the context–free grammars.

The syntax of SableCC is as follows:

[] []
[] []
[] []

As we can see from the grammar, a SableCC specification file may by an empty file. Remember the meaning of anything between [ and ] ? This means that anything between [ and ] can only be the empty string or a single occurrence of whatever is between the brackets.

From the allowed sections in a SableCC specification file, we will not take into account the because we will not use it. But if the reader is interested to know how it works, he should refer to [1]. We will now describe the syntax of the other productions. The is used to name the destination root, that is, the directory where all the generated files will be placed. We declare the name of the package as follows:

Package uk.co.brainycreatures.smallpascal;

In this example, the root directory will be uk\co\brainycreatures\smallpascal. Meaning that all the genarated subfolders will be placed under this directory. A package name may be any sequence of identifiers, starting with a letter followed by zero or more letters or digits, separated by a dot. The works like constants in Pascal. And, as its name imply, it is used as a helper of something, in this case the section (more about this next). To see how a is helpful let’s examine the following regular expression:

id = [‘a’ .. ‘z’] ([‘a’ .. ‘z’] | [‘0’ .. ‘9’])*

In this example we could simplify this regular expression by defining digit = [‘0’ .. ‘9’] and letter = [‘a’ .. ‘z’], then our regular expression ID becomes

id = letter (letter | digit)*

Letter and digit are our helpers. Here’s how we declare helpers in SableCC:

Helpers
letter = [‘a’ .. ‘z’];
digit = [‘0’ .. ‘9’];

After the follows the , which is where we define our terminals or tokens. Lets use the example above to show how to declare tokens in SableCC:

Tokens
id = letter (letter | digit)*;

This is very similar to regular expressions as described in the Regular Expressions tutorial. A token defined by a string of characters is declared between quotes, e.g. program = ‘program’, and every declaration ends with a semicolon. Following the is the , in other words, the section where we declare the tokens to be ignored by the parser. For example, comments, blanks, carriage return, etc.

Here’s an example:

Helpers
any_charater = [0x0 .. 0xfffff];
nl = ‘\n’;
Tokens
comment = ‘⁄⁄’ any_character nl
blank = 10 | 10 13 | 9;
Ignored Tokens
comment,
blank;

In this case, comment and blank will be ignored by the parser.

Finally, in the section, we declare the productions grammar for the language. The productions are defined in BNF or EBNF (Extended Backus–Naur Form).

Here’s an example of how to declare productions:

Tokens
identifier = letter (letter | digit)*;
number = digit+;
plus = ‘+’;
Productions
expression = identifier plus identifier minus number ;

As we can see it is similar to the context-free grammars presented in the grammars tutorial. The only difference is that in SableCC we don‘t declare a nonterminal surrounded by , and we replace the → by =. In the productions section, it is sometimes required to precede a token by T. and a production by a P. This happens when we have a token and a production with the same name. For example if we have a production program and a token program, then in the production below we must precede program by T., because will not know if it is the token program or the production program.

Tokens
program = ‘program’;
semicolon = ‘;’ ;
Productions
program = T.program identifier semicolon ;

Now that we are familiar with the syntax of SableCC, let’s implement our subset.

The root of our package will be uk.co.brainycreatures.smallpascal. Hence in the package declaration we will have

Package uk.co.brainycreatures.smallpascal;After defining the package, we are going to declare our helpers, which are shown below:

Helpers
a = ‘a’ | ‘A’ ;
b = ‘b’ | ‘B’ ;
e = ‘e’ | ‘E’ ;
g = ‘g’ | ‘G’ ;
i = ‘i’ | ‘I’ ;
m = ‘m’ | ‘M’;
n = ‘n’ | ‘N’ ;
o = ‘o’ | ‘O’ ;
p = ‘p’ | ‘P’ ;
r = ‘r’ | ‘R’ ;
t = ‘t’ | ‘T’;
v = ‘v’ | ‘V’ ;
w = ‘w’ | ‘W’;
cr = 13 ; ⁄⁄ carriage return
lf = 10 ; ⁄⁄ line feed
tab = 9 ; ⁄⁄ tab char
ascii_char = [32 .. 127] ;
blank = ‘’ ;
digit = [‘0’ .. ‘9’] ;
letter = [[‘a’ .. ‘z’] + [‘A’ .. ‘Z’]] ;
l_brace = ‘{’ ;
r_brace = ‘}’ ;
l_paren_star = ‘(*’ ;
r_paren_star = ‘*)’ ;

Note the space between the letters in the definition of the keywords. In this case we need them, because each letter is a helper token. So, in defining our tokens, we have a sequence of helpers separated by a space.

Finally, we will describe the grammar for the language. Here it is:

Productions
program = program_heading global_declaration_part begin main_statement_part end dot;
program_heading = {non_empty} program identifier semicolon | {empty} ;
global_declaration_part = var_declaration;
var_declaration = var var_decl+;
var_decl = identifier_list colon type;
identifier_list = {single} identifier | {sequence} identifier_list comma identifier;
type = integer;
main_statement_part = statement_sequence;
statement_sequence = {single} statement | {sequence} statement_sequence semicolon statement ;
statement = {assign} identifier assignop expression | {writeln} writeln_stmt ;
writeln_stmt = {simple}writeln | {arguments} writeln l_paren expression_list r_paren ;
expression_list = {single} expression | {sequence} expression_list comma expression ;
expression = {term} term | {plus} expression plus term | {minus} expression minus term ;
term = {factor} factor | {mult} term mult factor | {div} term div factor;
factor = {identifier} identifier | {integer} integer;

Generating the working classes

In this section we will implement the semantic analyser and the code generators for the language. We start by implementing the semantic analyser below.

The semantic analyser

The semantic analysis phase completes the static analysis (parsing) phase of the source program, by ensuring that the grammatically correct statements passed to it by the parser ‘make sense’. For instance, the syntax of assignment statements in Pascal does not require that the identifiers have been declared, that they are variables, that they are of similar type, and that the operations can be performed on those types. All these restrictions are defined by the static semantics of Pascal, and the semantic analyser performs the corresponding checks. To perform this checking, the semantic analyser completely processes the declarations and creates equivalent information known as property information. This information is stored in a data structure known as symbol table. The symbol table is used so that given any identifier the associated information may be found.

For this small subset, the only checking that needs to be performed is that the identifiers are declared before use and that they are declared only once. There is no need for type checking because all the identifiers are of the same type. The semantic analysis is processed by a depth first traversal on the abstract syntax tree, storing information about identifiers in the symbol table after having visited the subtrees of the production containing the variables declaration. These information will then be fetched as we encounter identifiers in productions factor and variable, respectively.

Now that we have the required information to implement the semantic analyser, we will now describe how to implement it. Returning to the grammar above, we are now going to explain the effect of the names between { and } in the alternatives of each production. The name of each alternative in the productions of the grammar above are prefixed by A and concatenated by the name of the production to produce a type name for the alternative. For example, for the production below

Factor =           {identifier} identifier | {integer} integer_literal | … ;

a type named AIdentifierFactor and a type name AIntegerFactor will be produced.

Given the information above, we design our semantic analyser by defining the methods for the alternatives including identifiers. That is, alternatives ASingleIdentifierList, ASequenceIdentifierList, AAssignStatement and AIdentifierFactor. Also, we need to process alternative AVarDecl. Note that for the alternative for var_decl doe not have a name. This is allowed in SableCC if the production is defined by one alternative only, and the resulting type is the name of the production prefixed by A.

Here is the implementation of our semantic analyser:

package uk.co.brainycreatures.smallpascal;

import uk.co.brainycreatures.smallpascal.node.*;
import uk.co.brainycreatures.smallpascal.analysis.*;
import java.util.*; // for the Hashtable

public class SemanticAnalyzer extends DepthFirstAdapter {
    // stores the identifiers being defined
    Hashtable symbol_table = new Hashtable(); // create a new table

    // check if the identifier is already in the table and report an error
    // if it is
    public void outASingleIdentifierList(AidentifierList node) {
        // identifier to be stored in the symbol table
        TIdentifier ident = node.getIdentifier();
    
        // name of the identifier to be stored in the table
        String key = ident.getText().toUpperCase(); //is the identifier in the table?

        if (symbol_table.containsKey(key)) { // report an error
            System.out.println("Identifier already defined.");
            System.exit(0);
        }
        else {
            symbol_table.put(key, key);
        }
    }

    // same as above
    public void outASingleIdentifierList(AidentifierList node) {
        // identifier to be stored in the symbol table
        TIdentifier ident = node.getIdentifier();

        // name of the identifier to be stored in the table
        String key = ident.getText().toUpperCase(); // is the identifier in the table?

        if (symbol_table.containsKey(key)) { // report an error
            System.out.println("Error: [" ident.getLine() + "," + ident.getPos() + "] Identifier already defined.");
            System.exit(0);
        }
        else {
            symbol_table.put(key, key);
        }
    }

    // checks if the identifier in the assignment statement was previously
    // declared, and report an error if it wasn’t
    public void outAAssignStatement(AAssignStatement node) {
        Tidentifier ident = node.getIdentifier();
        String key = ident.getText().toUpperCase();

        // Is the identifier in the table?
        // if not report error
        if (!symbol_table.containsKey(key)) {
            System.out.println("Error: [" + ident.getLine() + "," ident.getPos() + "] Unknown identifier.");
            System.exit(0);
        }
    }

    // same as above
    public void outAIdentifierFactor(AIdentifierFactor node) {
        Tidentifier ident = node.getIdentifier();
        String key = ident.getText().toUpperCase();

        // Is the identifier in the table?
        // if not report error
        if (!symbol_table.containsKey(key)) {
            System.out.println("Error: [" + ident.getLine() + "," + ident.getPos() + "] Unknown identifier.");
            System.exit(0);
        }
    }
}

We define the class SemanticAnalyser as extending class DepthFirstAdapter.

This automatically provides a depth–first traversal of the AST. To check the declarations of identifiers we just need methods outASingleIdentifierList and outASequenceIdentifierList. To check if they were declared before use we just need methods outAAssignStatement and outAIdentifierFactor. For more about how SableCC generates this methods refer to chapters 5 and 6 of [1]. We are now done with the semantic analyser. For our code generation we do the same thing, but we generate code as well as we process the identifiers in the symbol table.

The Class Generator

The ClassGenerator is also a subclass of class DepthFirstAdapter. To implement the code generator we need to do a depth first traversal of the tree, just as we did with the semantic analyser. But for the code generator we will use alternatives from other productions as well. For example, we will use the program_heading to define the name of the class to be generated. That is, we will use the name of the identifier that follows the program keyword, if the program heading is present in the program. If the program heading is not present in the program, the name of the class will be a.out, just as the executables generated by a C compiler in a Unix based system. We will now start the description of the design of our code generator. For example, from the production

program_heading =
{non_empty} T.program identifier semicolon |
{empty} ;

we will define methods outANonEmptyProgramHeading and outAEmptyProgramHeading. To generate a class file using the Jas API we need to create an instance of class ClassEnv first. Then we set the class attributes: its name, its super class, the source from which it is being compiled and its access modifiers. This is all done in the methods outlined above. We also need to create a default constructor. Although we don’t realise, the Java compiler generates a default constructor for us. This is how it looks in Java assembly:

.method public ()V
aload_0
invokevirtual java⁄lang⁄Object⁄()V
return
.end method

To define this method, we can either do it in production program_heading, or even before production program traverses its subtrees. More precisely, on the definition of its inAProgram method. It is up to the programmer’s preference. In our case, we are going to define the constructor in methodinAProgram. To define a method we need to define an instance of class CodeAttr, then we add the instructions with its addInsn method. We also need to create a new instance of class CodeAttr for object main_code. Here is how we implement it:

public void inAProgram(AProgram node) {
    // create an new instance of CodeAttr
    init = new CodeAttr();
    main_code = new CodeAttr(); // create a new object for main program body

    try {
        // define the method
        init.addInsn(new Insn(opc_aload_0));
        init.addInsn(new Insn(opc_invokevirtual, new MethodCP("java/lang/Object", "<init>", "()V")));
        init.addInsn(new Insn(opc_return));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

After defining the method, we must add it to the class. This will be done in either method outAEmptyProgramHeading or outANonEmptyProgramHeading. Here is its implementation:

public void outANonEmptyProgramHeading(ANonEmptyProgramHeading node) {
    // name of the class to be generated
    class_name = node.getIdentifier().getText();

    try {
        // set class attributes
        main_class.setClass((new ClassCP(class_name));
        main_class.setSuper(new ClassCP("java/lang/Object"));

        // source from which it is compiled
        main_class.setSource(new SourceAttr(source_file_name));

        // make class public
        main_class.setClassAccess((short) ACC_PUBLIC);

        // add the constructor method to the class
        main_class.addMethod((short) ACC_PUBLIC, , "<init>", "()V", init, null);
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

The code is self explanatory, so we won’t go into more details.

The code for method outAEmptyProgramHeading is similar to the code above, except for the name of the class. It sets class_name to “a.out”. Proceeding with the grammar, we are now going to define the code that processes identifiers; that is, the code that defines the identifiers. Pascal variables will be defined as Java fields. To define fields in the Jas API, we use method addField of class ClassEnv. The name of the generated variables will be in lowercase. We don’t need a symbol table in order to fetch the signature of a field. Rather, we use the name of the class to which it belongs. Here is the implementation:

public void outASingleIdentifierList(AsingleIdentifierList node) {
    // get the name of the name of the variable in lower case
    String var_name = node.getIdentifier().getText().toLowerCase();

    try {
        // add the field to the class
        main_class.addField(new Var((short) (ACC_STATIC | ACC_PUBLIC)), new AsciiCP(var_name), new AsciiCP("I"), new ConstAttr(new IntegerCP(0)))));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

The code above is equivalent to the following Java declaration:

public static int = 0;where is the same as the variable being declared.

The code for method outASequenceIdentifierList is the same as the code foroutASingleIdentifierList above. So, there is no need to describe it again.

Lets now describe how we implement the statements. Before going any further lets analyse how we translate Pascal statements into Java assembly. Because the Java Virtual Machine is stack based, we need to think in terms of stack operations only. So to translate an assignment statement we push all the factors on the right hand side to the stack and then we pop the result into the variable on the left hand side. For example, the assignment statement

a := b + cis translated to the following Java assembly code:

getstatic <class name>⁄b I
getstatic <class name>⁄c I
iadd
putstatic <class name>⁄a I</td>

where is the name of the class to which each of the fields a, b and c belong. In Java we use the ‘.’, but in Java assembly we use the ‘/’ to access either methods or fields. The I following the name of the variable means that this variable is of type int. The instruction getstatic is used to get the value of static fields of a class, and the instruction putstatic is used to store values into static fields of a class. The other arithmetic instructions that will be used in the code generation of the compiler for this subset are:

isub – used to subtract integer numbers. This instruction expects to find two numbers on
the top of the stack, and if they are there it pops them, subtracts them and pushes
the result onto the top of the stack.imul – the same as isub, but it multiplies insteadidiv – used to divide integers

In order to generate the appropriate code, we need to traverse the subtrees of the corresponding operator, and after visiting them we generate the opcode corresponding to this same operator. We need to push numbers and identifiers as we encounter them in production factor, and then generate the opcode for the arithmetic instruction in the alternatives of productions expression and term. Here are the implementations:

public void caseAIdentifierFactor(AidentifierFactor node) {
    String var_name = node.getIdentifier().getText().toLowerCase();
    
    try {
        // getstatic <class_name>⁄<var_name> I
        code.addInsn(new Insn(opc_getstatic, new FieldCP(class_name, var_name, "I")));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for integer factors:

public void caseAIntegerFactor(AIntegerFactor node) {
    // get the string value
    String num_image = node.getIdentifier().getText();

    int value = Integer.parseInt(num_image);

    try {
        switch(value) {
            case –1 :
                code.addInsn(new Insn(opc_iconst_m1)); break;
            case 0:
                code.addInsn(new Insn(opc_iconst_0)); break;
            case 1 :
                code.addInsn(new Insn(opc_iconst_1)); break;
            case 2:
                code.addInsn(new Insn(opc_iconst_2)); break;
            case 3:
                code.addInsn(new Insn(opc_iconst_3)); break;
            case 4:
                code.addInsn(new Insn(opc_iconst_4)); break;
            case 5:
                code.addInsn(new Insn(opc_iconst_5)); break;
            default:
                if (value => –128 && value <= 127) {
                    code.addInsn(new Insn(opc_bipush, value));
                }
                else if (value >= –65536 && value <= 65535) {
                    code.addInsn(new Insn(opc_sipush, value));
                }
                else {
                    code.addInsn(new Insn(opc_ldc, value));
                }
                break;
        }
    }
}

Code for addition operator:

public void outAPlusExpression(APlusExpression node) {
    try {
        code.addInsn(new Insn(opc_iadd));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for subtraction operator:

public void outAMinusExpression(AMinusExpression node) {
    try {
        code.addInsn(new Insn(opc_isub));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for multiplication operator:

public void outAMultTerm(AMultTerm node) {
    try {
        code.addInsn(new Insn(opc_imul));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for division operator:

public void outADivTerm(ADivTerm node) {
    try {
        code.addInsn(new Insn(opc_idiv));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for assignment statement:

public void outAAssignStatement(AassignStatement node) {
    String var_name = node.getIdentifier().getText().toLowerCase();
    
    try {
        code.addInsn(new Insn(opc_putstatic, new FieldCP(class_name, var_name, "I")));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

Code for writeln statement:

public void inAWritelnStatement(AwritelnStatement node) {
    try {
        code.addInsn(new Insn(opc_getstatic, new FieldCP("java/lang/Object", "out", "Ljava/io/PrintStream;")));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

public void outAWritelnStatement(AwritelnStatement node) {
    try {
        code.addInsn(new Insn(opc_invokevirtual, new MethodCP("java/io/PrintStream", "println", "(I)V")));
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

After generating all the code, we add an extra return instruction, we add the method to the class, and we dump the class. This is done in method outAProgram, that is at the exit of a program. The code is a follows:

public void outAProgram(AProgram node) {
    try {
        // add return instruction to the end of the method
        code.addInsn(new Insn(opc_return);

        // add main method to main_class
        main_class.addMethod((short)( ACC_PUBLIC | ACC_STATIC), "main", "([Ljava/lang/String;)V", code, null));// generate class file
        main_class.write(new DataOutputStream(new FileOutputStream(class_name + ".class"))); // output status

        System.out.println("Wrote " + class_name + ".class"]");
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

The Main class

Now that we have implemented the working classes, it time to implement the Main class. This class simply activates the lexer, parser, semantic analyser and the class generator. This is always the same for every compiler or interpreter implemented using SableCC. The code for the main class is shown below:

package uk.co.brainycreatures.smallpascal;

import uk.co.brainycreatures.smallpascal.semantic.SemanticAnalyser;
import uk.co.brainycreatures.smallpascal.code.ClassGenerator;
import uk.co.brainycreatures.smallpascal.parser.*;
import uk.co.brainycreatures.smallpascal.lexer.*;
import uk.co.brainycreatures.smallpascal.node.*;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        long start_time, stop_time; // times compilation

        if (args.length < 1) {
            System.out.println("Usage:");
            System.out.println(" java uk.co.brainycreatures.smallpascal.Main <filename>");
        }

        try {
            start_time = System.currentTimeMillis();// create lexer
            Lexer lexer = new Lexer (new PushbackReader(new BufferedReader(new FileReader(args[0])), 1024));    // parser program
            Parser parser = new Parser(lexer);
            Start ast = parser.parse(); // check program semantics
            ast.apply(new SemanticAnalyser());  // generate class file
            ast.apply(new ClassGenerator());
        }
        catch (Exception e) {
            System.out.println(e);
        }
    }
}

Now that we have implemented everything, it is time to compile all the source code. Assuming that the current working directory is in our CLASSPATH, we compile the compiler as follows:

C:\mycompilers\javac uk\co\brainycreatures\Main.java

If no errors were found in the source, we will have all the sources compiled. We then just have to debug the compiler. That is, test it with small pascal programs. The best way to test a compiler is by inputig erroneous data to it. That is, invalid programs.

If you have any queries, please do not hesitate to send an email at fidel dot viegas at brainycreatures dot co dot uk

Best Regards
Fidel.

References:

  1. Etienne Gagnon,”SableCC, An Object–Oriented Compiler Framework”, Master’s thesis, McGill University, Montreal, Quebec, March 1998.

Programmers with little or no exposure to parallelism have an opportunity to learn about multicore programming at the UPCRC Illinois Summer School to be held July 25-29, 2011 at the University of Illinois at Urbana-Champaign.

The week-long, intensive workshop will provide a solid foundation in the fundamentals of multicore programming, offer hands-on experience with the use of multicore languages and libraries, and introduce emerging research topics. Upon completion, participants will be equipped to choose the best multicore programming model for current and future projects.

Prerequisites for the summer school include solid programming experience (C, C++, C# or Java languages) and a demonstrated interest in applying multicore programming to academic or professional pursuits.

For more information about the summer school, visit the website at http://www.upcrc.illinois.edu/summer/2011/index.html.

 

Cheri Helregel, Outreach Coordinator
UPCRC Illinois
University of Illinois at Urbana-Champaign
Ph: (217) 244-6097
url | www.upcrc.illinois.edu

 

Compartilho com vocês resumo que fiz sobre meus estudos na disciplina de Segurança em Redes ministrada pelo prof. Paulo Lício do IC/UNICAMP. Peço desculpas pela linguagem usada.


FTP

O protocolo FTP utiliza principalmente as portas 21 e 20 para se comunicar. Dados de controle (comandos) são enviados na conexão estabelecida ao servidor na porta 21 e dados brutos (transf. de arquivos, saída de listagem) são transferidos utilizando uma conexão aberta via porta 20. É possível instrumentar o servidor a operar em um de dois modos possíveis: Modo passivo e Modo ativo.

No modo Ativo o cliente informa a qual das suas portas o servidor deve se conectar. Este é o modo default.

No modo Passivo o cliente deve informar ao servidor para entrar neste modo e o servidor irá responder com uma confirmação e o número de porta ao qual o cliente deverá abrir uma conexão para transferir os dados. Sempre o ataque
aparecerá nos logs como tendo fonte no servidor FTP.

FTP Bounce Leitura

Os ataques de FTP Bounce são todos baseados no fato de pode instruir o servidor a abrir uma conexão em um IP diferente do que enviou o comando. Em geral as duas máquinas alvo possuem uma relação de confiança ou no minimo estão na mesma rede alvo.

O ataque de leitura tem os seguintes passos:

1) atacante C conecta ao servidor A.
2) atacante C diz para o servidor A se conectar à máquina B para enviar dados.
3) atacante C pede para armazenar o arquivo ‘xyz’.
4) servidor A le o arquivo ‘xyz’ da máquina B.
5) máquina B envia o arquivo para o servidor pois possui relação de confiança.

com isso o atacante consegue fazer com que a máquina alvo B ‘leia’ um
arquivo do servidor de ftp.

FTP Bounce Escrita

O ataque de escrita tem os seguintes passos:

1) atacante C conecta ao servidor A.
2) atacante C envia um arquivo ‘abc’ qualquer ao servidor A.
3) atacante C diz para o servidor A se conectar à máquina B para enviar dados.
4) atacante C pede para obter o arquivo ‘abc’.
5) servidor A envia para máquina B o arquivo xyz.
6) máquina B aceita o arquivo do servidor pois possui relação de confiança.

com isso o atacante consegue gravar qualquer arquivo que quiser na máquina alvo B.

FTP Bounce Scan

O ataque de scan tem os seguintes passos:

1) atacante C conecta ao servidor A.
2) atacante C diz para o servidor A se conectar à máquina B porta x para enviar dados.
3) atacante C pede para obter o arquivo ‘abc’.
4) servidor A tenta se conectar a máquina B na porta x. Se conseguir enviar o arquivo
reporta sucesso e atacante percebe que a porta x estava aberta. Se ocorrer erro
no envio é porque a porta estava fechada.
5) atacante C muda número da porta x <- y e repete-se os passos 2, 3, 4 e 5.

com isso o atacante consegue gravar qualquer arquivo que quiser na máquina alvo B.

Fonte: Slides apresentados em aula: http://www.las.ic.unicamp.br/paulo/cursos/segc/2011s1/

Compartilho com vocês resumo que fiz sobre meus estudos na disciplina de Segurança em Redes ministrada pelo prof. Paulo Lício do IC/UNICAMP. Peço desculpas pela linguagem usada.


Meio de transmissão via rádio usando o protocolo IEEE 802.11x.

CSMA/CA

CSMA/CD -> Ethernet; Todo mundo escuta o sinal;
CSMA/CA -> Rádio; Devido a alcance do sinal, algumas máquinas podem não estar ouvindo.

O protocolo CSMA/CD é usado em ethernet, meio físico e todo mundo que está no mesmo enlace ouve os pacotes que estão sendo transmitidos. Logo quando há uma colisão, por que qualquer duas máquinas no cabo estão querendo transmitir ao mesmo tempo, estas máquinas conseguem perceber a colisão (ouvindo o cabo e chegando se o que ouviram é igual ao que enviaram) e aguardam um tempo aleatório para voltar a tentar a transmissão. Porém no protocolo CSMA/CA (transmissão a rádio) a coisa é diferente. Pode ser que uma máquina A esteja transmitindo dados para uma máquina B, e outra máquina C não esteja ciente desta situação porque nao esta ao alcance dos dados transmitidos por A. Se C começar a fazer uma transferencia ele irá “corromper” a conexão em andamento e também não conseguirá transmitir os seus proprios dados. Para evitar isso o CSMA/CA usa um mecanismo de permissão de transmissao. Quando uma máquina A deseja se comunicar com uma outra máquina B ela envia um sinal de RTS (request to send) para B. Se B puder ouvir a transmissão naquele momento ela envia um sinal de CLS (clear to send) contendo a quantidade de dados que ela está disposta a ouvir. Só então a máquina A começa a fazer a transmissão e as máquinas que podem se comunicar com B sabem que devem aguardar por uma quantidade de tempo necessária para que B consiga ouvir a quantidade de dados que avisou.

Modo de Operação

Uma rede sem fio pode operar em dois modos: modo ad-hoc e modo infraestrutura. No modo infraestrutura existe uma antena que atua como um roteador da rede sem fio e esta é responsável por atender aos clientes, logo todos se conectam a ela. No modo ad-hoc não existe um ponto central, a rede estabelecida é ponto a ponto, isto é, todos os clientes estão conectados diretamente entre-si. Por ex: no modo infraestrutura para duas máquinas conversarem elas devem enviar os dados por meio do AP. No modo ad-hoc estas máquinas podem se comunicar diretamente. Embora é comum no modo ad-hoc uma das máquinas atuar como roteador para acesso a internet.

O protocolo WEP

Wire Equivalent Privacy. Foi a primeira tentativa de protocolo criada para se comunicar em rede sem fio. Devido a falhas de implementação o protocolo não oferece: integridade, autenticidade nem controle de acesso. A criptografia utilizada é baseada em uma chave compartilhada de 40 ou 104 bits + um vetor de inicialização de 24 bits aleatórios gerados a cada pacote transmitido. Devido ao vetor de inicialização ser transmitido em ‘claro’ pela rede é possível dado dois pacotes cifrados com o mesmo IV obter o conteúdo ‘claro’ dos dois pacotes e ainda obter o fluxo de bits (usado no algoritmo RC4) usado para criptografar os pacotes. De posse desse fluxo de bits é possível injetar qualquer pacote na rede alvo.

O protocolo WPA/WPA2

O protocolo WPA surgiu dada a urgente necessidade de substituição do protocolo WEP. Ele é apenas um subconjunto dos requisitos implementados no protocolo WPA2 e teve grande aceitação pois trazia mais seguranção do que o WEP e ‘rodava’ no mesmo hardware.

WPA2 implementa um mecanismo onde a chave criptografica utilizada é temporária; a chave é alternada de 2 em 2 horas por exemplo, portanto torna difícil para o atacante conseguir um volume de dados suficiente para fazer criptoanalise em um tempo hábil para que a senha ainda seja válida.

No entanto, ambos os protocolos são sujeitos a ataques.

Ataque a Rede Sem Fio

Escuta (Eavesdropping)

A escuta em rede sem fio é uma coisa implicita do meio de transmissão pois não impoe limite para o sinal transmitido. Um atacante pode escutar todo o trafego que está sendo transmitido pela rede. Portanto se os dados são enviados em ‘claro’ qualquer um pode captura-los. Mesmo que este trafego esteja sendo transmitido criptografado, os endereços de enlace não são criptografados dando possibilidade de um ataque de personificação de endereço de enlace.

Detecção

Para fazer a escuta o atacante coloca sua interface em modo promiscuo, com isso ele consegue visualizar todo o trafego que a sua placa de rede capturar. Uma vez neste modo o atacante consegue capturar os probes request/response (enviados no momento da associação e a cada 10min) e beacons enviados pelos APs/estaçoes. É até mesmo possível forçar as máquinas a enviarem probes request/response através de um ataque de desassociação.

Ataque ao WEP

Quebra da Senha

Aplicar o ataque de força bruta na senha de 40 bits é algo totalmente viável hoje em dia. E para facilitar o ataque de força bruta é possível utilizar de fraquesas da implementação da criptografia WEP. A cada pacote enviado na rede é usado um novo IV, porém existem ‘apenas’ 2^24 possibilidades de IV’s. Portanto é factível de dois ou mais pacotes usarem o mesmo IV. Dado que eu tenho 2 pacotes cifrados com o mesmo fluxo, obtem-se facilmente o texto em claro! Em outras palavras,
obtendo dois pacotes cifrados com o mesmo IV (o qual é enviado em claro pela rede) já que a senha não muda! O conteúdo dos pacotes pode ser obtido pois os pacotes geralmente apresentam uma estrutura rígida o bastante em suas porçoes iniciais, então é possível se ter uma bom ’chute’ de qual o seu conteúdo! Uma vez tendo o texto claro destas mensagens é possível determinar qual foi o fluxo gerado pelo algoritmo RC4 e daí qual foi o par key+IV usado para gerar este fluxo!

Geração de Trafego

Para se obter dois pacotes cifrados com o mesmo par key+iv é necessário que seja gerado muito! trafego na rede. Para forçar a geração de trafego o atacante pode capturar os gratuitious arp’s enviados pelas estações no momento em que elas se associam ao AP e modificar estes ARP’s para que eles se tornem ARP Request! Daí envia-os ad-infinitum na rede alvo.
Veja que devido a limitações da criptografia e da previsibilidade do cabecalho do pacote arp, é possível fazer esta alteração no pacote sem descriptografa-lo!

 Injeção de Pacotes

Pacotes DHCP e ARP possuem uma estrutura e conteúdo previsivel, uma vez que o atacante tem o conteúdo em claro de uma mensagem é trivial descobrir o fluxo usado para criptografa-la (faze-se um XOR da mensagem cifrada e texto claro). Uma vez obtido o fluxo RC4 o atacante pode usa-lo para criptografar qualquer pacote e enseri-lo na rede. Para descobrir o texto claro de um pacote o atacante pode ‘chutar’ o conteúdo do pacote (já que este possui uma estrutura e conteúdo previsivel) ou o atacante pode fazer um ping na rede interna via internet!

Negação de Serviço

O atacante finge ser o AP e envia requisiçoes de desassociaçÃo para as máquinas. Não é necessário criptografia!

Man in the Middle

é necessário que a rede seja desprotegida ou que o atacante conheça a chave WEP. Daí o atacante pode se registrar como AP com mesmo nome do AP original ou ele pode derrubar o AP original e assumir o seu lugar.

Proteção de Rede Wireless

Ocultar a rede: Nao resolve muita coisa, o atacante pode descobrir a rede analisando o trafego de beacos e probes.

Filtrar endereço MAC: Nao resolve muita coisa, o atacante pode obter os pacotes trafegando na rede sem fio onde os endereços de enlace trafegam em claro. Daí é só fazer um spoofing.

Usar boa implementação de IV: Nao resolve muita coisa. Mesmo que a geração dos IV’s seja aleatória, em algum momento eles vão repetir pois existe um número limitado de opçoes.

Alterar senha: Nao resolve o problema. Um atacante pode demorar minutos para quebrar uma senha. O administrador vai alterar a senha a cada minuto?

Usar protocolo 802.1x: Cria outro problema. O servidor não é autenticado perante os usuários. Portanto o atacante pode se fazer passar pelo servidor.

Cuidados no nível de IP: Utilizar SSH e/ou IPSec. Adicionam mais segurança mas também adicionam overhead.

WPA2: Acredito ser a melhor alternativa.

Fonte: Slides apresentados em aula: http://www.las.ic.unicamp.br/paulo/cursos/segc/2011s1/

Olá,

seguindo a linha de assunto do post anterior hoje eu compartilho com vocês o link para uma ferramenta que eu achei incrivel!

Você já se perguntou como uma máquina virtual consegue ler e executar as instruções do programa (compilado!) que você quer emular? Interessante não? O Pin [1] não é exatamente uma máquina virtual mas em um certo nível ele é, porém ele faz mais que isso, ele te permite fazer a instrumentação (inspecção e modificação) do código binário que está sendo executado! Por exemplo: saber o endereço de memória que os programas estão acessando, saber quantas instruções – em linguagem de montagem – foram executadas, saber quais foram as instruções executadas, etc.

Nesse link [2] você encontra vários exemplos bem bacanas, vale a pena dar uma olhada =D. Fico devendo para um futuro post um tutorial sobre como utilizar o Pin.

Cheers.
JT.

[1] http://www.pintool.org/
[2] http://www.pintool.org/docs/39599/Pin/html/

Olá amigo fuçador!

Você sabia que é possível (por enquanto só conheço no linux) carregar uma biblioteca que permite fazer a sobrecarga das funções do seu programa executando no seu linux? Não? Pois existe e eu achei isso excelente!

As possibilidades do que você pode fazer com isto são várias! Por exemplo, competindo na maratona de programação eu sempre quis saber como que era feito o cálculo de quanto de memória um dado programa executou. Utilizando o recurso mencionado é simples basta você sobrecarregar a chamada da função malloc!

Nesse link [1] você vai encontrar um tutorial simples e prático de como começar a utilizar esta funcionalidade!

Divirta-se!

John.

[1] http://blog.jorgepereira.com.br/2010/06/11/conhecendo-e-utilizando-a-ld_preload/

Blog no WordPress.com. | Tema: Motion até volcanic.
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.